matrx-orm 3.1.170__tar.gz → 3.1.172__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.
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/PKG-INFO +1 -1
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/core/rls_session.py +22 -6
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/secrets_battery/FEATURE.md +3 -1
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/secrets_battery/items.py +105 -11
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/pyproject.toml +1 -1
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/test_vault_aggregate_recovery_stateful.py +120 -3
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/.gitignore +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/AGENTS.md +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/BASE_CLASS_METHODS.md +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/CLAUDE.md +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/MODEL_API.md +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/README.md +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/database/orm/extended/managers/ai_model_base.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/docs/migrations.md +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/FEATURE.md +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/MODULE_README.md +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/__init__.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/adapters/MODULE_README.md +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/adapters/__init__.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/adapters/async_postgresql.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/adapters/base_adapter.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/adapters/postgrest_client_adapter.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/adapters/supabase_adapter.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/admin/FEATURE.md +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/admin/__init__.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/admin/router.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/api/__init__.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/api/auth.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/api/config.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/api/handlers.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/api/protocol.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/api/server.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/associations.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/cache_debug.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/catalog.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/catalog_sql.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/client/MODULE_README.md +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/client/__init__.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/client/postgres_connection.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/client/postgrest.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/client/supabase_auth.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/client/supabase_config.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/client/supabase_manager.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/core/EXTENDED-TASK.md +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/core/FEATURE.md +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/core/MODULE_README.md +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/core/RELATIONS-TASKS.md +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/core/TASKS.md +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/core/__init__.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/core/async_db_manager.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/core/base.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/core/config.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/core/connections.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/core/db_function.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/core/diagnostics.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/core/expressions.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/core/extended.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/core/fields.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/core/introspection.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/core/listen.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/core/loop_filters.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/core/model_dto.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/core/model_view.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/core/one_database.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/core/paginator.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/core/pool_maintenance.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/core/pool_watch.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/core/pooler_role.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/core/pydantic_bridge.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/core/registry.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/core/relations.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/core/resilience.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/core/session_context.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/core/signals.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/core/tls.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/core/transaction.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/core/types.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/core/write_queue.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/core/write_retry.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/entity.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/error_handling.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/exceptions.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/extended/__init__.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/extended/app_error_handler.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/identity_rule.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/local_sqlite.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/middleware/__init__.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/middleware/base.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/migrations/MODULE_README.md +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/migrations/__init__.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/migrations/cli.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/migrations/ddl.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/migrations/diff.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/migrations/executor.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/migrations/integration.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/migrations/loader.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/migrations/operations.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/migrations/state.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/migrations/table_filter.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/no_org_assignment.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/operations/FEATURE.md +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/operations/MODULE_README.md +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/operations/__init__.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/operations/bulk_update_values.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/operations/catalog_select.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/operations/conflict.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/operations/conflict_writes.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/operations/create.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/operations/db_functions.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/operations/delete.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/operations/dynamic_admin.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/operations/dynamic_crud.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/operations/graph_walk.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/operations/latest_rows.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/operations/queue_claim.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/operations/read.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/operations/relational_backfill.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/operations/staging_load.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/operations/update.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/platform_access.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/platform_db.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/py.typed +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/pytest_lock_guard.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/pytest_plugin.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/python_sql/MODULE_README.md +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/python_sql/__init__.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/python_sql/db_objects.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/python_sql/table_detailed_relationships.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/python_sql/table_typescript_relationship.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/query/FEATURE.md +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/query/MODULE_README.md +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/query/__init__.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/query/builder.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/query/executor.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/resources/__init__.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/resources/additional-assigners-2026-09-12.json +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/resources/org_assignment_trigger_baseline.json +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/schema_builder/FEATURE.md +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/schema_builder/MODULE_README.md +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/schema_builder/__init__.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/schema_builder/code_handler.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/schema_builder/codegen_writer.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/schema_builder/columns.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/schema_builder/common.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/schema_builder/diff_preview.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/schema_builder/entity_capabilities.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/schema_builder/generator.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/schema_builder/helpers/__init__.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/schema_builder/helpers/base_generators.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/schema_builder/helpers/entity_generators.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/schema_builder/helpers/git_checker.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/schema_builder/package_wiring.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/schema_builder/relationships.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/schema_builder/runner.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/schema_builder/schema.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/schema_builder/schema_manager.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/schema_builder/tables.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/schema_builder/views.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/secrets_battery/__init__.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/secrets_battery/attachments.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/secrets_battery/crypto.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/secrets_battery/host_authorization.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/secrets_battery/model.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/secrets_battery/mutation_receipts.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/secrets_battery/native_enable.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/secrets_battery/native_lifecycle.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/secrets_battery/native_passkeys.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/secrets_battery/native_source.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/secrets_battery/org_service.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/secrets_battery/service.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/session/README.md +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/session/__init__.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/session/coalesce.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/session/dag.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/session/errors.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/session/fallback.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/session/flush.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/session/lifecycle.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/session/managed.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/session/op.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/session/reads.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/session/session.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/session/telemetry.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/sql_executor/MODULE_README.md +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/sql_executor/__init__.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/sql_executor/executor.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/sql_executor/queries.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/sql_executor/registry.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/sql_executor/types.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/sql_executor/utils.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/state.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/utils/__init__.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/utils/sql_utils.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/utils/type_converters.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/matrx_orm/write_actor.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/scripts/git-branches.sh +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/scripts/publish.sh +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/scripts/release.sh +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/MODULE_README.md +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/__init__.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/conftest.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/MODULE_README.md +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/__init__.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_adapter_registry.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_admin_db_columns.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_admin_response_rendering.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_admin_search_negation.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_admin_search_sentinels.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_advisory_lock.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_agent_message.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_api_auth.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_api_config.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_api_handlers.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_api_protocol.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_array_agg_order_by.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_associations.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_associations_wiring.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_bulk_hydration_offload.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_bulk_update_by_pk.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_bulk_upsert_increment_set_fields.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_cache_debug.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_call_function.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_case_when.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_catalog.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_committed_row_guard.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_composite_pk_filter.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_composite_pk_write_paths.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_config.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_conflict_target.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_conflict_writes.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_connection_codecs.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_connection_poison_guard.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_connection_primitives.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_count_composite_distinct.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_count_limit_param_leak.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_db_function_field.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_ddl_generator.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_deadlock_retry.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_derived_table.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_dirty_tracking.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_dynamic_crud.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_exceptions.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_execute_query_integrity_mapping.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_expression_primitives.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_fields.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_filter_jsonb_agg_array_index.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_from_alias.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_fts.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_func_expression_args.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_graph_walk.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_having_annotate_alias.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_insert_ignore_and_admin_primitives.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_introspect_rls_policies.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_join_and_aggregate.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_latest_rows.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_listen_notify.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_local_sqlite.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_lock_contention_retry.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_managed_write_guard.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_matrx_entity.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_migration_diff_types.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_migration_loader.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_model_instance.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_model_meta.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_optimistic_concurrency.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_order_by_expression.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_org_assignment_inventory.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_orm_banners_are_client_safe.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_parameter_name_hydration.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_patch_jsonb_path.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_persisted_count.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_pool_holder_census.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_pool_loop_recreation.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_pool_maintenance.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_postgrest_filters.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_pydantic_bridge.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_query_builder.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_query_error_detail.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_query_executor_sql.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_query_timeout.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_querybuilder_clone_semantics.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_queue_claim.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_read_connection_retry.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_registry.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_registry_multi_database.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_registry_multi_schema.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_relational_backfill.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_relations.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_resilience.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_rls_session_scope.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_schema_exists.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_session_advisory_lock.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_standalone_write_session_context.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_state_cache.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_subquery_filter_raw.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_subquery_in_filter.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_subquery_nested_expressions.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_supabase_auth.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_supabase_config.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_tls.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_transaction_database_identity.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_typed_join_projection.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_typed_predicates.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_update_case_expression.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_update_subquery.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_update_with_rebase.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_upsert_default_leak.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_upsert_with_conflict.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_values_jsonb_decode.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_watched_lifecycle.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_write_queue_deadline.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level1/test_write_retry_semantics.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level2/MODULE_README.md +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level2/__init__.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level2/conftest.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level2/test_bulk_ops.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level2/test_cache_integration.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level2/test_crud.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level2/test_foreign_keys.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level2/test_m2m.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level2/test_manager.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level2/test_migrations_live.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level2/test_query_execution.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level2/test_schema_diff.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/level2/verify_committed_row_guard_pg17.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/live_pooler_role.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/native_source_cases.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/native_source_oracle.rs +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/sample_project/.env.example +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/sample_project/README.md +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/sample_project/__init__.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/sample_project/generate.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/sample_project/generated/.gitkeep +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/sample_project/matrx_orm.yaml +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/sample_project/test_schema_generation.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/sample_project_desktop/.env.example +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/sample_project_desktop/README.md +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/sample_project_desktop/__init__.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/sample_project_desktop/client_example.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/sample_project_desktop/client_example.ts +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/sample_project_desktop/client_supabase_example.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/sample_project_desktop/server.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/schema/entity_tests.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/schema/test_base_generation.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/schema/test_composite_pk_fk_generation.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/schema/test_composite_unique_generation.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/schema/test_dto_identity_generation.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/schema/test_entity_capability_generation.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/schema/test_enum_ordering.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/schema/test_generate_schema.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/schema/test_junction_analysis.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/schema/test_multi_schema_output.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/schema/test_schema_analysis.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/schema/test_sql_expression_defaults.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/schema/test_table_relationships_query.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/schema/test_view_generation.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/schema_builder/test_array_default_parsing.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/schema_builder/test_focused_generation_plan.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/schema_builder/test_host_models_aggregator.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/session/__init__.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/session/conftest.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/session/test_cache_no_downgrade.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/session/test_capture_poison_proof.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/session/test_coalesce.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/session/test_dag.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/session/test_disk_spill_fallback.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/session/test_flush_individual_fallback.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/session/test_governed_write.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/session/test_managed.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/session/test_op_enqueue_site.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/session/test_reads.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/session/test_record_error_bounded_insert.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/session/test_record_error_storm.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/session/test_session.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/session/test_spill_guard_plugin.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/test_array_arg_element_type.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/test_attachment_mutation_serialization.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/test_database_name_alias.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/test_db_functions.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/test_dynamic_select_unregistered.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/test_immutable_insert_or_exact_verify.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/test_keyed_digest.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/test_model_cls_refactor.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/test_native_enable.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/test_native_lifecycle.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/test_native_lifecycle_fences.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/test_native_passkey_receipts.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/test_native_source.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/test_no_blocking_asyncpg_tls.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/test_one_database.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/test_order_by_desc_nulls_last.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/test_pooler_role_guard.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/test_secret_audit_names_its_organization.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/test_secrets_battery.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/test_secrets_minting_keys.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/test_shared_lock_guard.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/test_sql_param_casts.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/tests/test_sync_twins_guard.py +0 -0
- {matrx_orm-3.1.170 → matrx_orm-3.1.172}/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.
|
|
3
|
+
Version: 3.1.172
|
|
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
|
|
@@ -98,12 +98,28 @@ async def rls_session(
|
|
|
98
98
|
details={"role": role},
|
|
99
99
|
)
|
|
100
100
|
# Set the verified claims FIRST (while still the privileged role), then drop to
|
|
101
|
-
# the restricted role. Order mirrors PostgREST;
|
|
102
|
-
# survive the role switch within the
|
|
103
|
-
|
|
101
|
+
# the restricted role. Order mirrors PostgREST; all of them are transaction-local
|
|
102
|
+
# (`set_config(..., true)` IS `SET LOCAL`) and survive the role switch within the
|
|
103
|
+
# same transaction.
|
|
104
|
+
#
|
|
105
|
+
# ONE ROUND TRIP, NOT FOUR. These were four sequential `execute` calls, and a
|
|
106
|
+
# round trip to a hosted pooler is not free: measured 2026-09-19 against the main
|
|
107
|
+
# database, the three that always run cost 0.247 + 0.085 + 0.105 = 0.437 s
|
|
108
|
+
# separately and 0.192 s as one statement — so every RLS session on the platform
|
|
109
|
+
# paid a quarter of a second to say three things it could say at once. They are
|
|
110
|
+
# emitted in the SAME ORDER inside one target list, which is the order that
|
|
111
|
+
# matters: the claims land before the role drops. `SET LOCAL ROLE x` and
|
|
112
|
+
# `set_config('role', 'x', true)` are the same GUC write, so the role still
|
|
113
|
+
# changes here and nowhere else, and `role` stays LAST.
|
|
114
|
+
settings: list[str] = ["set_config('request.jwt.claims', $1, true)"]
|
|
115
|
+
bound: list[str] = [claims_json]
|
|
104
116
|
if statement_timeout_ms is not None:
|
|
105
|
-
|
|
117
|
+
bound.append(str(int(statement_timeout_ms)))
|
|
118
|
+
settings.append(f"set_config('statement_timeout', ${len(bound)}, true)")
|
|
106
119
|
if lock_timeout_ms is not None:
|
|
107
|
-
|
|
108
|
-
|
|
120
|
+
bound.append(str(int(lock_timeout_ms)))
|
|
121
|
+
settings.append(f"set_config('lock_timeout', ${len(bound)}, true)")
|
|
122
|
+
bound.append(role)
|
|
123
|
+
settings.append(f"set_config('role', ${len(bound)}, true)")
|
|
124
|
+
await conn.execute("select " + ", ".join(settings), *bound)
|
|
109
125
|
yield
|
|
@@ -66,7 +66,7 @@ remains atomic.
|
|
|
66
66
|
|
|
67
67
|
**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.
|
|
68
68
|
|
|
69
|
-
**Native lifecycle component (`native_lifecycle.py`).** The shared coordinator must supply locked parent/child projections from the exported field constants, current host management authority and its active transaction. Inspection locks the exact native metadata child; component delete/restore mutate only that child's tombstone and lifecycle. Deletion accepts active and restored-quarantined sources; recovery compares aware ISO manifest timestamps to database timestamps. Personal metadata provenance is enforced by the database guard, never inferred from the request scope. Coordinator/preview integration and actual aggregate database acceptance remain pending; these helpers alone do not enable protected recovery. Contract: [native lifecycle](../../../../../common-docs/projects/credential-sharing-browser-login/NATIVE-LIFECYCLE.md).
|
|
69
|
+
**Native lifecycle component (`native_lifecycle.py`).** The shared coordinator must supply locked parent/child projections from the exported field constants, current host management authority and its active transaction. Inspection locks the exact native metadata child; component delete/restore mutate only that child's tombstone and lifecycle. Deletion accepts active and restored-quarantined sources; recovery compares aware ISO manifest timestamps to database timestamps, then parses the already-validated manifest instant only at the typed native restore mutation boundary. Personal metadata provenance is enforced by the database guard, never inferred from the request scope. Coordinator/preview integration and actual aggregate database acceptance remain pending; these helpers alone do not enable protected recovery. Contract: [native lifecycle](../../../../../common-docs/projects/credential-sharing-browser-login/NATIVE-LIFECYCLE.md).
|
|
70
70
|
|
|
71
71
|
**`inject_into_sandbox=true` REQUIRES an env alias — refused otherwise.** A
|
|
72
72
|
container environment is a NAME→value map, so a field with `key IS NULL` has
|
|
@@ -365,6 +365,8 @@ delete).
|
|
|
365
365
|
|
|
366
366
|
## Change Log
|
|
367
367
|
|
|
368
|
+
- **2026-09-19** — Restored native lifecycle metadata with the parsed, timezone-aware manifest instant at the typed mutation boundary, preserving the manifest string for aggregate integrity checks.
|
|
369
|
+
|
|
368
370
|
- **2026-09-19** — Added transaction-owned native lifecycle component operations, quarantine deletion and aware timestamp validation; aggregate coordinator integration remains open.
|
|
369
371
|
|
|
370
372
|
- **2026-09-19** — Added `native_enable.py`, a transaction-required, value-free
|
|
@@ -896,17 +896,26 @@ async def delete_credential_item(
|
|
|
896
896
|
raw = await _lock_item_raw(item_id)
|
|
897
897
|
from .attachments import soft_delete_item_attachments
|
|
898
898
|
|
|
899
|
+
# Keep the native lifecycle's complete locked projection here. The
|
|
900
|
+
# classifier deliberately owns the metadata lock, while the aggregate
|
|
901
|
+
# coordinator remains the only author of parent/source changes.
|
|
902
|
+
from .native_lifecycle import (
|
|
903
|
+
NATIVE_LIFECYCLE_ATTACHMENT_FIELDS,
|
|
904
|
+
NATIVE_LIFECYCLE_FIELD_FIELDS,
|
|
905
|
+
delete_native_component,
|
|
906
|
+
inspect_native_lifecycle,
|
|
907
|
+
)
|
|
908
|
+
|
|
899
909
|
fields = await (
|
|
900
910
|
UserSecret.filter(credential_item_id=item_id, deleted_at__isnull=True)
|
|
901
|
-
.select_for_update().order_by("id")
|
|
902
|
-
.values("id", "is_active", "user_id", "organization_id")
|
|
911
|
+
.select_for_update().order_by("id").values(*NATIVE_LIFECYCLE_FIELD_FIELDS)
|
|
903
912
|
)
|
|
904
913
|
for child in fields:
|
|
905
914
|
if child.get("user_id") != raw.get("user_id") or child.get("organization_id") != raw.get("organization_id"):
|
|
906
915
|
raise CredentialRestoreConflictError("credential field ownership does not match parent")
|
|
907
916
|
attachments = await (
|
|
908
917
|
CredentialAttachment.filter(credential_item_id=item_id, deleted_at__isnull=True)
|
|
909
|
-
.select_for_update().order_by("id").values(
|
|
918
|
+
.select_for_update().order_by("id").values(*NATIVE_LIFECYCLE_ATTACHMENT_FIELDS)
|
|
910
919
|
)
|
|
911
920
|
# A legacy field grant can otherwise survive a parent delete and become
|
|
912
921
|
# usable if a future path reactivates the field.
|
|
@@ -916,6 +925,15 @@ async def delete_credential_item(
|
|
|
916
925
|
).values("id")]
|
|
917
926
|
if bound_field_ids:
|
|
918
927
|
await UserSecretGrant.delete_where(user_secret_id__in=bound_field_ids)
|
|
928
|
+
native_component = await inspect_native_lifecycle(
|
|
929
|
+
{**raw, "deleted_at": None}, fields, attachments, actor_id=actor_id,
|
|
930
|
+
audit_organization_id=audit_organization_id, state="live", deletion_timestamp=None,
|
|
931
|
+
)
|
|
932
|
+
if native_component is not None:
|
|
933
|
+
await delete_native_component(
|
|
934
|
+
native_component, parent_id=item_id, actor_id=actor_id,
|
|
935
|
+
audit_organization_id=audit_organization_id, deleted_at=now,
|
|
936
|
+
)
|
|
919
937
|
await soft_delete_item_attachments(item_id, actor_id=actor_id, deleted_at=now)
|
|
920
938
|
await UserSecret.update_where(
|
|
921
939
|
{"credential_item_id": item_id, "deleted_at__isnull": True},
|
|
@@ -924,7 +942,7 @@ async def delete_credential_item(
|
|
|
924
942
|
updated_at=now,
|
|
925
943
|
)
|
|
926
944
|
lifecycle = dict(raw.get("lifecycle") or {})
|
|
927
|
-
|
|
945
|
+
manifest: dict[str, Any] = {
|
|
928
946
|
"schema_version": 1,
|
|
929
947
|
"deletion_id": str(uuid.uuid4()),
|
|
930
948
|
"deleted_at": now.isoformat(),
|
|
@@ -933,6 +951,13 @@ async def delete_credential_item(
|
|
|
933
951
|
"attachments": [{"id": str(row["id"])} for row in attachments],
|
|
934
952
|
"state": "deleted",
|
|
935
953
|
}
|
|
954
|
+
if native_component is not None:
|
|
955
|
+
manifest["schema_version"] = 2
|
|
956
|
+
manifest["native_passkeys"] = [{
|
|
957
|
+
"id": native_component.passkey_id,
|
|
958
|
+
"source_field_id": native_component.source_field_id,
|
|
959
|
+
}]
|
|
960
|
+
lifecycle["vault_recovery"] = manifest
|
|
936
961
|
await CredentialItem.update_where(
|
|
937
962
|
{"id": item_id, "deleted_at__isnull": True},
|
|
938
963
|
deleted_at=now,
|
|
@@ -956,12 +981,13 @@ async def delete_credential_item(
|
|
|
956
981
|
|
|
957
982
|
def _recovery_manifest(raw: dict[str, Any], deletion_id: str) -> dict[str, Any]:
|
|
958
983
|
recovery = dict(raw.get("lifecycle") or {}).get("vault_recovery")
|
|
959
|
-
|
|
984
|
+
manifest_v1_keys = {
|
|
960
985
|
"schema_version", "deletion_id", "deleted_at", "prior_status",
|
|
961
986
|
"fields", "attachments", "state",
|
|
962
987
|
}
|
|
963
|
-
if not isinstance(recovery, dict) or type(recovery.get("schema_version")) is not int or recovery.get("schema_version")
|
|
988
|
+
if not isinstance(recovery, dict) or type(recovery.get("schema_version")) is not int or recovery.get("schema_version") not in {1, 2}:
|
|
964
989
|
raise CredentialRestoreConflictError("automatic recovery is unavailable for this deletion")
|
|
990
|
+
manifest_keys = manifest_v1_keys | ({"native_passkeys"} if recovery["schema_version"] == 2 else set())
|
|
965
991
|
if set(recovery) != manifest_keys:
|
|
966
992
|
raise CredentialRestoreConflictError("deletion record has unexpected or missing fields")
|
|
967
993
|
if not isinstance(recovery.get("deletion_id"), str) or recovery.get("deletion_id") != deletion_id:
|
|
@@ -1009,6 +1035,23 @@ def _recovery_manifest(raw: dict[str, Any], deletion_id: str) -> dict[str, Any]:
|
|
|
1009
1035
|
attachment_ids.append(entry["id"])
|
|
1010
1036
|
if len(set(field_ids)) != len(field_ids) or len(set(attachment_ids)) != len(attachment_ids):
|
|
1011
1037
|
raise CredentialRestoreConflictError("deletion record contains duplicate components")
|
|
1038
|
+
if recovery["schema_version"] == 2:
|
|
1039
|
+
native = recovery.get("native_passkeys")
|
|
1040
|
+
if not isinstance(native, list) or len(native) != 1:
|
|
1041
|
+
raise CredentialRestoreConflictError("native deletion record is invalid")
|
|
1042
|
+
entry = native[0]
|
|
1043
|
+
if not isinstance(entry, dict) or set(entry) != {"id", "source_field_id"}:
|
|
1044
|
+
raise CredentialRestoreConflictError("native deletion record is invalid")
|
|
1045
|
+
try:
|
|
1046
|
+
passkey_id, source_id = entry["id"], entry["source_field_id"]
|
|
1047
|
+
if not isinstance(passkey_id, str) or not isinstance(source_id, str):
|
|
1048
|
+
raise ValueError
|
|
1049
|
+
if str(uuid.UUID(passkey_id)) != passkey_id or str(uuid.UUID(source_id)) != source_id:
|
|
1050
|
+
raise ValueError
|
|
1051
|
+
except (KeyError, TypeError, ValueError) as exc:
|
|
1052
|
+
raise CredentialRestoreConflictError("native deletion record is invalid") from exc
|
|
1053
|
+
if source_id not in field_ids:
|
|
1054
|
+
raise CredentialRestoreConflictError("native source is absent from deletion fields")
|
|
1012
1055
|
parent_deleted_at = raw.get("deleted_at")
|
|
1013
1056
|
if recovery["state"] == "deleted":
|
|
1014
1057
|
if parent_deleted_at is None or _iso(parent_deleted_at) != recovery["deleted_at"]:
|
|
@@ -1031,7 +1074,7 @@ async def restore_credential_item(
|
|
|
1031
1074
|
async with transaction(PACKAGE_DB_NAME):
|
|
1032
1075
|
raw = await _lock_restore_item_raw(item_id)
|
|
1033
1076
|
manifest = _recovery_manifest(raw, deletion_id)
|
|
1034
|
-
if manifest["state"] == "restored":
|
|
1077
|
+
if manifest["state"] == "restored" and manifest["schema_version"] == 1:
|
|
1035
1078
|
item = await get_credential_item(item_id)
|
|
1036
1079
|
if item is None: # pragma: no cover
|
|
1037
1080
|
raise CredentialRestoreConflictError("restore state is inconsistent")
|
|
@@ -1043,19 +1086,49 @@ async def restore_credential_item(
|
|
|
1043
1086
|
raise CredentialRestoreConflictError("deletion record is incomplete")
|
|
1044
1087
|
field_ids = sorted(entry["id"] for entry in field_entries)
|
|
1045
1088
|
attachment_ids = sorted(entry["id"] for entry in attachment_entries)
|
|
1089
|
+
from .native_lifecycle import (
|
|
1090
|
+
NATIVE_LIFECYCLE_ATTACHMENT_FIELDS,
|
|
1091
|
+
NATIVE_LIFECYCLE_FIELD_FIELDS,
|
|
1092
|
+
inspect_native_lifecycle,
|
|
1093
|
+
restore_native_component,
|
|
1094
|
+
)
|
|
1046
1095
|
fields = await UserSecret.filter(id__in=field_ids).select_for_update().order_by("id").values(
|
|
1047
|
-
|
|
1096
|
+
*NATIVE_LIFECYCLE_FIELD_FIELDS
|
|
1048
1097
|
) if field_ids else []
|
|
1049
1098
|
attachments = await CredentialAttachment.filter(id__in=attachment_ids).select_for_update().order_by("id").values(
|
|
1050
|
-
|
|
1099
|
+
*NATIVE_LIFECYCLE_ATTACHMENT_FIELDS
|
|
1051
1100
|
) if attachment_ids else []
|
|
1052
1101
|
if {str(row["id"]) for row in fields} != set(field_ids) or {str(row["id"]) for row in attachments} != set(attachment_ids):
|
|
1053
1102
|
raise CredentialRestoreConflictError("a deleted component is no longer retained")
|
|
1103
|
+
all_fields = await UserSecret.filter(credential_item_id=item_id).select_for_update().order_by("id").values(
|
|
1104
|
+
*NATIVE_LIFECYCLE_FIELD_FIELDS
|
|
1105
|
+
)
|
|
1106
|
+
unrecorded_protected = [
|
|
1107
|
+
field for field in all_fields
|
|
1108
|
+
if field.get("execution_purpose") != GENERAL_EXECUTION_PURPOSE and str(field.get("id")) not in set(field_ids)
|
|
1109
|
+
]
|
|
1110
|
+
if unrecorded_protected:
|
|
1111
|
+
raise CredentialRestoreUnsupportedError("unrecorded protected component needs its owning recovery flow")
|
|
1112
|
+
if manifest["state"] == "restored":
|
|
1113
|
+
expected = manifest["native_passkeys"][0]
|
|
1114
|
+
native_component = await inspect_native_lifecycle(
|
|
1115
|
+
raw, all_fields, attachments, actor_id=actor_id,
|
|
1116
|
+
audit_organization_id=audit_organization_id, state="live", deletion_timestamp=None,
|
|
1117
|
+
)
|
|
1118
|
+
if native_component is None or (
|
|
1119
|
+
native_component.passkey_id != expected["id"]
|
|
1120
|
+
or native_component.source_field_id != expected["source_field_id"]
|
|
1121
|
+
):
|
|
1122
|
+
raise CredentialRestoreConflictError("native component no longer matches this deletion")
|
|
1123
|
+
item = await get_credential_item(item_id)
|
|
1124
|
+
if item is None: # pragma: no cover
|
|
1125
|
+
raise CredentialRestoreConflictError("restore state is inconsistent")
|
|
1126
|
+
return item, 0, 0, True
|
|
1054
1127
|
for field in fields:
|
|
1055
1128
|
if (str(field.get("credential_item_id")) != item_id or field.get("user_id") != raw.get("user_id")
|
|
1056
1129
|
or field.get("organization_id") != raw.get("organization_id") or _iso(field.get("deleted_at")) != deleted_at):
|
|
1057
1130
|
raise CredentialRestoreConflictError("a field no longer matches this deletion")
|
|
1058
|
-
if field.get("execution_purpose") != GENERAL_EXECUTION_PURPOSE or field.get("field_key") == PROTECTED_FIELD_KEY:
|
|
1131
|
+
if manifest["schema_version"] == 1 and (field.get("execution_purpose") != GENERAL_EXECUTION_PURPOSE or field.get("field_key") == PROTECTED_FIELD_KEY):
|
|
1059
1132
|
raise CredentialRestoreUnsupportedError("passkey or protected field recovery needs its native recovery flow")
|
|
1060
1133
|
if field.get("field_key") == "totp_seed":
|
|
1061
1134
|
await require_host_authorization(
|
|
@@ -1065,6 +1138,27 @@ async def restore_credential_item(
|
|
|
1065
1138
|
for attachment in attachments:
|
|
1066
1139
|
if str(attachment.get("credential_item_id")) != item_id or _iso(attachment.get("deleted_at")) != deleted_at:
|
|
1067
1140
|
raise CredentialRestoreConflictError("an attachment no longer matches this deletion")
|
|
1141
|
+
native_component = None
|
|
1142
|
+
if manifest["schema_version"] == 2:
|
|
1143
|
+
native_component = await inspect_native_lifecycle(
|
|
1144
|
+
raw, fields, attachments, actor_id=actor_id, audit_organization_id=audit_organization_id,
|
|
1145
|
+
state="deleted", deletion_timestamp=deleted_at,
|
|
1146
|
+
)
|
|
1147
|
+
if manifest["schema_version"] == 2:
|
|
1148
|
+
expected = manifest["native_passkeys"][0]
|
|
1149
|
+
if native_component is None or (
|
|
1150
|
+
native_component.passkey_id != expected["id"]
|
|
1151
|
+
or native_component.source_field_id != expected["source_field_id"]
|
|
1152
|
+
):
|
|
1153
|
+
raise CredentialRestoreConflictError("native component no longer matches this deletion")
|
|
1154
|
+
if native_component is not None:
|
|
1155
|
+
# The manifest persists an exact ISO string for integrity comparison;
|
|
1156
|
+
# the typed native mutation owns a timestamptz equality predicate.
|
|
1157
|
+
native_deleted_at = datetime.fromisoformat(deleted_at)
|
|
1158
|
+
await restore_native_component(
|
|
1159
|
+
native_component, parent_id=item_id, actor_id=actor_id,
|
|
1160
|
+
audit_organization_id=audit_organization_id, deleted_at=native_deleted_at,
|
|
1161
|
+
)
|
|
1068
1162
|
# Do not turn a hidden uniqueness violation into a generic DB error.
|
|
1069
1163
|
# Field-key identity is local to its credential item; environment
|
|
1070
1164
|
# aliases are separately unique in the item's owner namespace.
|
|
@@ -1107,7 +1201,7 @@ async def restore_credential_item(
|
|
|
1107
1201
|
await record_secret_audit(action="item_restored_quarantined", actor_id=actor_id,
|
|
1108
1202
|
organization_id=audit_organization(record_organization_id=raw.get("organization_id"), request_organization_id=audit_organization_id, what=f"restore_credential_item(item_id={item_id})"),
|
|
1109
1203
|
user_id=str(raw["user_id"]) if raw.get("user_id") else None,
|
|
1110
|
-
metadata={"credential_item_id": item_id, "deletion_id": deletion_id, "restored_fields": len(fields), "restored_attachments": len(attachments)})
|
|
1204
|
+
metadata={"credential_item_id": item_id, "deletion_id": deletion_id, "restored_fields": len(fields), "restored_attachments": len(attachments), "restored_native_passkeys": 1 if manifest["schema_version"] == 2 else 0})
|
|
1111
1205
|
item = await get_credential_item(item_id)
|
|
1112
1206
|
if item is None: # pragma: no cover
|
|
1113
1207
|
raise CredentialRestoreConflictError("restored item is unavailable")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "matrx-orm"
|
|
3
|
-
version = "3.1.
|
|
3
|
+
version = "3.1.172"
|
|
4
4
|
description = "Async-first PostgreSQL ORM with bidirectional migrations, schema introspection, many-to-many relationships, and built-in state caching"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = { text = "MIT" }
|
|
@@ -16,6 +16,7 @@ import pytest
|
|
|
16
16
|
|
|
17
17
|
from matrx_orm.secrets_battery import attachments as attachments_mod
|
|
18
18
|
from matrx_orm.secrets_battery import items as items_mod
|
|
19
|
+
from matrx_orm.secrets_battery import native_lifecycle
|
|
19
20
|
from matrx_orm.secrets_battery import service as service_mod
|
|
20
21
|
|
|
21
22
|
|
|
@@ -26,6 +27,7 @@ INACTIVE_FIELD = "00000000-0000-0000-0000-000000000012"
|
|
|
26
27
|
OLD_FIELD = "00000000-0000-0000-0000-000000000013"
|
|
27
28
|
ATTACHMENT = "00000000-0000-0000-0000-000000000021"
|
|
28
29
|
OLD_ATTACHMENT = "00000000-0000-0000-0000-000000000022"
|
|
30
|
+
PASSKEY = "00000000-0000-0000-0000-000000000031"
|
|
29
31
|
|
|
30
32
|
|
|
31
33
|
class _Query:
|
|
@@ -57,6 +59,9 @@ class _Query:
|
|
|
57
59
|
elif key.endswith("__in"):
|
|
58
60
|
if row.get(key[:-4]) not in expected: # type: ignore[operator]
|
|
59
61
|
return False
|
|
62
|
+
elif isinstance(row.get(key), datetime) and isinstance(expected, str):
|
|
63
|
+
if row[key].isoformat() != expected: # type: ignore[union-attr]
|
|
64
|
+
return False
|
|
60
65
|
elif row.get(key) != expected:
|
|
61
66
|
return False
|
|
62
67
|
return True
|
|
@@ -119,10 +124,11 @@ class _Store:
|
|
|
119
124
|
"value_hint": "c", "value_version": 1, "is_active": False, "description": None,
|
|
120
125
|
"created_at": old, "updated_at": old, "deleted_at": old},
|
|
121
126
|
],
|
|
122
|
-
|
|
127
|
+
"attachments": [
|
|
123
128
|
{"id": ATTACHMENT, "credential_item_id": ITEM, "deleted_at": None, "updated_at": now},
|
|
124
129
|
{"id": OLD_ATTACHMENT, "credential_item_id": ITEM, "deleted_at": old, "updated_at": old},
|
|
125
130
|
],
|
|
131
|
+
"passkeys": [],
|
|
126
132
|
"grants": [
|
|
127
133
|
{"credential_item_id": ITEM, "user_secret_id": None, "user_id": "member"},
|
|
128
134
|
{"credential_item_id": None, "user_secret_id": ACTIVE_FIELD, "user_id": "legacy-member"},
|
|
@@ -167,6 +173,12 @@ def vault(monkeypatch: pytest.MonkeyPatch) -> _Store:
|
|
|
167
173
|
monkeypatch.setattr(items_mod, "record_secret_audit", audit)
|
|
168
174
|
monkeypatch.setattr(attachments_mod, "record_secret_audit", audit)
|
|
169
175
|
monkeypatch.setattr(items_mod, "audit_organization", lambda **_kwargs: "audit-org")
|
|
176
|
+
passkeys = type("Passkeys", (_Model,), {"table": "passkeys", "store": store})
|
|
177
|
+
monkeypatch.setattr(native_lifecycle, "CredentialItem", item)
|
|
178
|
+
monkeypatch.setattr(native_lifecycle, "UserSecret", field)
|
|
179
|
+
monkeypatch.setattr(native_lifecycle, "PasskeyCredential", passkeys)
|
|
180
|
+
monkeypatch.setattr(native_lifecycle, "get_active_connection", lambda: object())
|
|
181
|
+
monkeypatch.setattr(native_lifecycle, "require_host_authorization", _native_allow)
|
|
170
182
|
return store
|
|
171
183
|
|
|
172
184
|
|
|
@@ -190,11 +202,9 @@ async def test_actual_delete_then_restore_quarantines_only_children_in_manifest(
|
|
|
190
202
|
assert all(row["deleted_at"].isoformat() == deleted_at for row in vault.tables["fields"][:2]) # type: ignore[union-attr]
|
|
191
203
|
assert vault.tables["attachments"][0]["deleted_at"].isoformat() == deleted_at # type: ignore[union-attr]
|
|
192
204
|
assert vault.tables["grants"] == []
|
|
193
|
-
|
|
194
205
|
restored, fields, attachments, already = await items_mod.restore_credential_item(
|
|
195
206
|
ITEM, deletion_id=deletion_id, actor_id="owner", audit_organization_id="audit-org"
|
|
196
207
|
)
|
|
197
|
-
|
|
198
208
|
assert (fields, attachments, already) == (2, 1, False)
|
|
199
209
|
assert restored.status == "disabled"
|
|
200
210
|
assert parent["deleted_at"] is None and parent["access_mode"] == "restricted"
|
|
@@ -211,6 +221,113 @@ async def test_actual_delete_then_restore_quarantines_only_children_in_manifest(
|
|
|
211
221
|
assert vault.tables["grants"] == []
|
|
212
222
|
|
|
213
223
|
|
|
224
|
+
@pytest.mark.asyncio
|
|
225
|
+
async def test_native_v2_coordinator_deletes_and_restores_exact_pair_with_auxiliaries(
|
|
226
|
+
vault: _Store, monkeypatch: pytest.MonkeyPatch
|
|
227
|
+
) -> None:
|
|
228
|
+
"""The aggregate coordinator, not a helper-only caller, owns v2 custody."""
|
|
229
|
+
vault.tables["items"][0].update(definition_key="native_passkey", access_mode="restricted")
|
|
230
|
+
source = vault.tables["fields"][0]
|
|
231
|
+
source.update(
|
|
232
|
+
field_key="passkey_private", execution_purpose="passkey_private", handling="sealed",
|
|
233
|
+
editable=False, inject_into_sandbox=False, key=None, value_hint=None,
|
|
234
|
+
)
|
|
235
|
+
vault.tables["passkeys"] = [{
|
|
236
|
+
"id": PASSKEY, "credential_item_id": ITEM, "source_field_id": ACTIVE_FIELD,
|
|
237
|
+
"organization_id": "owner-personal-org", "lifecycle": "saved_waiting_for_site",
|
|
238
|
+
"deleted_at": None,
|
|
239
|
+
}]
|
|
240
|
+
passkeys = type("Passkeys", (_Model,), {"table": "passkeys", "store": vault})
|
|
241
|
+
monkeypatch.setattr(native_lifecycle, "CredentialItem", items_mod.CredentialItem)
|
|
242
|
+
monkeypatch.setattr(native_lifecycle, "UserSecret", items_mod.UserSecret)
|
|
243
|
+
monkeypatch.setattr(native_lifecycle, "PasskeyCredential", passkeys)
|
|
244
|
+
monkeypatch.setattr(native_lifecycle, "get_active_connection", lambda: object())
|
|
245
|
+
|
|
246
|
+
async def authorize(*_args: object, **_kwargs: object) -> None:
|
|
247
|
+
return None
|
|
248
|
+
|
|
249
|
+
monkeypatch.setattr(native_lifecycle, "require_host_authorization", authorize)
|
|
250
|
+
deletion_id = await _delete(vault)
|
|
251
|
+
manifest = vault.tables["items"][0]["lifecycle"]["vault_recovery"] # type: ignore[index]
|
|
252
|
+
assert manifest["schema_version"] == 2 # type: ignore[index]
|
|
253
|
+
assert manifest["native_passkeys"] == [{"id": PASSKEY, "source_field_id": ACTIVE_FIELD}] # type: ignore[index]
|
|
254
|
+
assert vault.tables["passkeys"][0]["lifecycle"] == "retired"
|
|
255
|
+
assert vault.tables["passkeys"][0]["deleted_at"] == vault.tables["items"][0]["deleted_at"]
|
|
256
|
+
expected_native_deleted_at = datetime.fromisoformat(manifest["deleted_at"]) # type: ignore[arg-type]
|
|
257
|
+
native_restore_timestamps: list[datetime] = []
|
|
258
|
+
original_restore_native_component = native_lifecycle.restore_native_component
|
|
259
|
+
|
|
260
|
+
async def restore_with_typed_timestamp(*args: object, **kwargs: object) -> None:
|
|
261
|
+
deleted_at = kwargs["deleted_at"]
|
|
262
|
+
assert isinstance(deleted_at, datetime)
|
|
263
|
+
assert deleted_at.tzinfo is not None and deleted_at.utcoffset() is not None
|
|
264
|
+
native_restore_timestamps.append(deleted_at)
|
|
265
|
+
await original_restore_native_component(*args, **kwargs) # type: ignore[arg-type]
|
|
266
|
+
|
|
267
|
+
monkeypatch.setattr(native_lifecycle, "restore_native_component", restore_with_typed_timestamp)
|
|
268
|
+
|
|
269
|
+
_, fields, attachments, already = await items_mod.restore_credential_item(
|
|
270
|
+
ITEM, deletion_id=deletion_id, actor_id="owner", audit_organization_id="audit-org"
|
|
271
|
+
)
|
|
272
|
+
assert (fields, attachments, already) == (2, 1, False)
|
|
273
|
+
assert native_restore_timestamps == [expected_native_deleted_at]
|
|
274
|
+
assert vault.tables["passkeys"][0]["deleted_at"] is None
|
|
275
|
+
assert vault.tables["passkeys"][0]["lifecycle"] == "saved_waiting_for_site"
|
|
276
|
+
assert vault.tables["fields"][0]["is_active"] is False
|
|
277
|
+
assert vault.tables["items"][0]["status"] == "disabled"
|
|
278
|
+
_, fields, attachments, already = await items_mod.restore_credential_item(
|
|
279
|
+
ITEM, deletion_id=deletion_id, actor_id="owner", audit_organization_id="audit-org"
|
|
280
|
+
)
|
|
281
|
+
assert (fields, attachments, already) == (0, 0, True)
|
|
282
|
+
vault.tables["fields"].append({
|
|
283
|
+
**vault.tables["fields"][0], "id": SECOND_ITEM, "field_key": "other_protected",
|
|
284
|
+
"deleted_at": None, "is_active": False,
|
|
285
|
+
})
|
|
286
|
+
before = deepcopy(vault.tables)
|
|
287
|
+
with pytest.raises(items_mod.CredentialRestoreUnsupportedError):
|
|
288
|
+
await items_mod.restore_credential_item(
|
|
289
|
+
ITEM, deletion_id=deletion_id, actor_id="owner", audit_organization_id="audit-org"
|
|
290
|
+
)
|
|
291
|
+
assert vault.tables == before
|
|
292
|
+
restore_audits = [event for event in vault.audits if event.get("action") == "item_restored_quarantined"]
|
|
293
|
+
assert restore_audits[-1]["metadata"]["restored_native_passkeys"] == 1 # type: ignore[index]
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
@pytest.mark.asyncio
|
|
297
|
+
async def test_native_v2_forged_pair_refuses_before_resurrecting_any_component(
|
|
298
|
+
vault: _Store, monkeypatch: pytest.MonkeyPatch
|
|
299
|
+
) -> None:
|
|
300
|
+
"""A forged native manifest cannot restore a parent, source, or auxiliary."""
|
|
301
|
+
vault.tables["items"][0].update(definition_key="native_passkey", access_mode="restricted")
|
|
302
|
+
vault.tables["fields"][0].update(
|
|
303
|
+
field_key="passkey_private", execution_purpose="passkey_private", handling="sealed",
|
|
304
|
+
editable=False, inject_into_sandbox=False, key=None, value_hint=None,
|
|
305
|
+
)
|
|
306
|
+
vault.tables["passkeys"] = [{
|
|
307
|
+
"id": PASSKEY, "credential_item_id": ITEM, "source_field_id": ACTIVE_FIELD,
|
|
308
|
+
"organization_id": "owner-personal-org", "lifecycle": "saved_waiting_for_site",
|
|
309
|
+
"deleted_at": None,
|
|
310
|
+
}]
|
|
311
|
+
passkeys = type("Passkeys", (_Model,), {"table": "passkeys", "store": vault})
|
|
312
|
+
monkeypatch.setattr(native_lifecycle, "CredentialItem", items_mod.CredentialItem)
|
|
313
|
+
monkeypatch.setattr(native_lifecycle, "UserSecret", items_mod.UserSecret)
|
|
314
|
+
monkeypatch.setattr(native_lifecycle, "PasskeyCredential", passkeys)
|
|
315
|
+
monkeypatch.setattr(native_lifecycle, "get_active_connection", lambda: object())
|
|
316
|
+
monkeypatch.setattr(native_lifecycle, "require_host_authorization", _native_allow)
|
|
317
|
+
deletion_id = await _delete(vault)
|
|
318
|
+
vault.tables["items"][0]["lifecycle"]["vault_recovery"]["native_passkeys"][0]["id"] = SECOND_ITEM # type: ignore[index]
|
|
319
|
+
before = deepcopy(vault.tables)
|
|
320
|
+
with pytest.raises(items_mod.CredentialRestoreConflictError):
|
|
321
|
+
await items_mod.restore_credential_item(
|
|
322
|
+
ITEM, deletion_id=deletion_id, actor_id="owner", audit_organization_id="audit-org"
|
|
323
|
+
)
|
|
324
|
+
assert vault.tables == before
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
async def _native_allow(*_args: object, **_kwargs: object) -> None:
|
|
328
|
+
return None
|
|
329
|
+
|
|
330
|
+
|
|
214
331
|
@pytest.mark.asyncio
|
|
215
332
|
@pytest.mark.parametrize("breakage", ["purged", "ownership_mismatch"])
|
|
216
333
|
async def test_actual_restore_refuses_changed_child_without_partial_resurrection(vault: _Store, breakage: str) -> None:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|