matrx-orm 3.1.176__tar.gz → 3.1.178__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.176 → matrx_orm-3.1.178}/PKG-INFO +2 -2
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/__init__.py +9 -1
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/catalog_sql.py +40 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/core/introspection.py +76 -2
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/session/fallback.py +39 -1
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/pyproject.toml +2 -2
- matrx_orm-3.1.178/tests/session/test_a_failed_capture_still_names_the_failure.py +144 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/session/test_record_error_bounded_insert.py +14 -2
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/.gitignore +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/AGENTS.md +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/BASE_CLASS_METHODS.md +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/CLAUDE.md +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/MODEL_API.md +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/README.md +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/database/orm/extended/managers/ai_model_base.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/docs/migrations.md +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/FEATURE.md +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/MODULE_README.md +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/adapters/MODULE_README.md +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/adapters/__init__.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/adapters/async_postgresql.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/adapters/base_adapter.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/adapters/postgrest_client_adapter.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/adapters/supabase_adapter.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/admin/FEATURE.md +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/admin/__init__.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/admin/router.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/api/__init__.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/api/auth.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/api/config.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/api/handlers.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/api/protocol.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/api/server.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/associations.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/cache_debug.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/catalog.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/client/MODULE_README.md +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/client/__init__.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/client/postgres_connection.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/client/postgrest.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/client/supabase_auth.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/client/supabase_config.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/client/supabase_manager.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/core/EXTENDED-TASK.md +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/core/FEATURE.md +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/core/MODULE_README.md +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/core/RELATIONS-TASKS.md +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/core/TASKS.md +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/core/__init__.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/core/async_db_manager.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/core/base.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/core/config.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/core/connections.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/core/db_function.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/core/diagnostics.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/core/expressions.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/core/extended.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/core/fields.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/core/listen.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/core/loop_filters.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/core/model_dto.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/core/model_view.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/core/one_database.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/core/paginator.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/core/pool_maintenance.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/core/pool_watch.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/core/pooler_role.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/core/pydantic_bridge.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/core/registry.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/core/relations.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/core/resilience.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/core/rls_session.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/core/session_context.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/core/signals.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/core/tls.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/core/transaction.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/core/types.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/core/write_queue.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/core/write_retry.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/entity.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/error_handling.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/exceptions.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/extended/__init__.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/extended/app_error_handler.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/identity_rule.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/local_sqlite.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/middleware/__init__.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/middleware/base.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/migrations/MODULE_README.md +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/migrations/__init__.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/migrations/cli.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/migrations/ddl.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/migrations/diff.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/migrations/executor.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/migrations/integration.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/migrations/loader.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/migrations/operations.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/migrations/state.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/migrations/table_filter.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/no_org_assignment.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/operations/FEATURE.md +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/operations/MODULE_README.md +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/operations/__init__.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/operations/bulk_update_values.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/operations/catalog_select.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/operations/conflict.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/operations/conflict_writes.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/operations/create.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/operations/db_functions.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/operations/delete.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/operations/dynamic_admin.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/operations/dynamic_crud.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/operations/graph_walk.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/operations/latest_rows.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/operations/queue_claim.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/operations/read.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/operations/relational_backfill.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/operations/staging_load.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/operations/update.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/platform_access.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/platform_db.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/py.typed +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/pytest_lock_guard.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/pytest_plugin.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/python_sql/MODULE_README.md +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/python_sql/__init__.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/python_sql/db_objects.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/python_sql/table_detailed_relationships.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/python_sql/table_typescript_relationship.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/query/FEATURE.md +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/query/MODULE_README.md +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/query/__init__.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/query/builder.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/query/executor.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/resources/__init__.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/resources/additional-assigners-2026-09-12.json +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/resources/org_assignment_trigger_baseline.json +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/schema_builder/FEATURE.md +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/schema_builder/MODULE_README.md +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/schema_builder/__init__.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/schema_builder/code_handler.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/schema_builder/codegen_writer.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/schema_builder/columns.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/schema_builder/common.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/schema_builder/diff_preview.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/schema_builder/entity_capabilities.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/schema_builder/generator.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/schema_builder/helpers/__init__.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/schema_builder/helpers/base_generators.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/schema_builder/helpers/entity_generators.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/schema_builder/helpers/git_checker.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/schema_builder/package_wiring.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/schema_builder/relationships.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/schema_builder/runner.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/schema_builder/schema.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/schema_builder/schema_manager.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/schema_builder/tables.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/schema_builder/views.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/secrets_battery/FEATURE.md +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/secrets_battery/__init__.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/secrets_battery/attachments.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/secrets_battery/backup_restore.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/secrets_battery/crypto.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/secrets_battery/host_authorization.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/secrets_battery/items.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/secrets_battery/model.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/secrets_battery/mutation_receipts.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/secrets_battery/native_enable.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/secrets_battery/native_lifecycle.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/secrets_battery/native_passkeys.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/secrets_battery/native_source.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/secrets_battery/org_service.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/secrets_battery/service.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/session/README.md +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/session/__init__.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/session/coalesce.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/session/dag.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/session/errors.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/session/flush.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/session/lifecycle.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/session/managed.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/session/op.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/session/reads.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/session/session.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/session/telemetry.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/sql_executor/MODULE_README.md +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/sql_executor/__init__.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/sql_executor/executor.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/sql_executor/queries.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/sql_executor/registry.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/sql_executor/types.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/sql_executor/utils.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/state.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/utils/__init__.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/utils/sql_utils.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/utils/type_converters.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/matrx_orm/write_actor.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/scripts/git-branches.sh +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/scripts/publish.sh +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/scripts/release.sh +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/MODULE_README.md +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/__init__.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/conftest.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/MODULE_README.md +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/__init__.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_adapter_registry.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_admin_db_columns.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_admin_response_rendering.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_admin_search_negation.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_admin_search_sentinels.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_advisory_lock.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_agent_message.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_api_auth.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_api_config.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_api_handlers.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_api_protocol.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_array_agg_order_by.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_association_trigger_actor_naming.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_associations.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_associations_wiring.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_bulk_hydration_offload.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_bulk_update_by_pk.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_bulk_upsert_increment_set_fields.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_cache_debug.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_call_function.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_case_when.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_catalog.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_committed_row_guard.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_composite_pk_filter.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_composite_pk_write_paths.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_config.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_conflict_target.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_conflict_writes.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_connection_codecs.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_connection_poison_guard.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_connection_primitives.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_count_composite_distinct.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_count_limit_param_leak.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_db_function_field.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_ddl_generator.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_deadlock_retry.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_derived_table.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_dirty_tracking.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_dynamic_crud.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_exceptions.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_execute_query_integrity_mapping.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_expression_primitives.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_fields.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_filter_jsonb_agg_array_index.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_from_alias.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_fts.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_func_expression_args.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_graph_walk.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_having_annotate_alias.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_insert_ignore_and_admin_primitives.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_introspect_rls_policies.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_join_and_aggregate.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_latest_rows.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_listen_notify.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_local_sqlite.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_lock_contention_retry.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_managed_write_guard.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_matrx_entity.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_migration_diff_types.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_migration_loader.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_model_instance.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_model_meta.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_optimistic_concurrency.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_order_by_expression.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_org_assignment_inventory.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_orm_banners_are_client_safe.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_parameter_name_hydration.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_patch_jsonb_path.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_percentile_cont.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_persisted_count.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_pool_holder_census.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_pool_loop_recreation.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_pool_maintenance.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_postgrest_filters.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_pydantic_bridge.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_query_builder.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_query_error_detail.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_query_executor_sql.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_query_timeout.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_querybuilder_clone_semantics.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_queue_claim.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_read_connection_retry.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_registry.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_registry_multi_database.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_registry_multi_schema.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_relational_backfill.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_relations.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_resilience.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_rls_session_scope.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_schema_exists.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_session_advisory_lock.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_standalone_write_session_context.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_state_cache.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_subquery_filter_raw.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_subquery_in_filter.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_subquery_nested_expressions.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_supabase_auth.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_supabase_config.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_tls.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_transaction_database_identity.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_typed_join_projection.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_typed_predicates.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_update_case_expression.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_update_subquery.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_update_with_rebase.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_upsert_default_leak.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_upsert_with_conflict.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_values_jsonb_decode.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_watched_lifecycle.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_write_queue_deadline.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level1/test_write_retry_semantics.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level2/MODULE_README.md +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level2/__init__.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level2/conftest.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level2/test_bulk_ops.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level2/test_cache_integration.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level2/test_crud.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level2/test_foreign_keys.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level2/test_m2m.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level2/test_manager.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level2/test_migrations_live.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level2/test_query_execution.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level2/test_schema_diff.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/level2/verify_committed_row_guard_pg17.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/live_pooler_role.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/native_source_cases.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/native_source_oracle.rs +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/sample_project/.env.example +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/sample_project/README.md +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/sample_project/__init__.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/sample_project/generate.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/sample_project/generated/.gitkeep +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/sample_project/matrx_orm.yaml +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/sample_project/test_schema_generation.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/sample_project_desktop/.env.example +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/sample_project_desktop/README.md +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/sample_project_desktop/__init__.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/sample_project_desktop/client_example.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/sample_project_desktop/client_example.ts +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/sample_project_desktop/client_supabase_example.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/sample_project_desktop/server.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/schema/entity_tests.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/schema/test_base_generation.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/schema/test_composite_pk_fk_generation.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/schema/test_composite_unique_generation.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/schema/test_dto_identity_generation.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/schema/test_entity_capability_generation.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/schema/test_enum_ordering.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/schema/test_generate_schema.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/schema/test_junction_analysis.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/schema/test_multi_schema_output.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/schema/test_schema_analysis.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/schema/test_sql_expression_defaults.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/schema/test_table_relationships_query.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/schema/test_view_generation.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/schema_builder/test_array_default_parsing.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/schema_builder/test_focused_generation_plan.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/schema_builder/test_host_models_aggregator.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/session/__init__.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/session/conftest.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/session/test_cache_no_downgrade.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/session/test_capture_poison_proof.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/session/test_coalesce.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/session/test_dag.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/session/test_disk_spill_fallback.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/session/test_flush_individual_fallback.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/session/test_governed_write.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/session/test_managed.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/session/test_op_enqueue_site.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/session/test_reads.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/session/test_record_error_storm.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/session/test_session.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/session/test_spill_guard_plugin.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/test_array_arg_element_type.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/test_attachment_mutation_serialization.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/test_backup_alias_conflict.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/test_backup_restore_live.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/test_database_name_alias.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/test_db_functions.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/test_dynamic_select_unregistered.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/test_immutable_insert_or_exact_verify.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/test_keyed_digest.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/test_model_cls_refactor.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/test_native_enable.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/test_native_lifecycle.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/test_native_lifecycle_fences.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/test_native_passkey_receipts.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/test_native_source.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/test_no_blocking_asyncpg_tls.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/test_one_database.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/test_order_by_desc_nulls_last.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/test_pooler_role_guard.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/test_secret_audit_names_its_organization.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/test_secrets_battery.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/test_secrets_minting_keys.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/test_shared_lock_guard.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/test_sql_param_casts.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/test_sync_twins_guard.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/test_user_visible_lookup.py +0 -0
- {matrx_orm-3.1.176 → matrx_orm-3.1.178}/tests/test_vault_aggregate_recovery_stateful.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.178
|
|
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
|
|
@@ -26,7 +26,7 @@ Requires-Dist: asyncpg>=0.31.0
|
|
|
26
26
|
Requires-Dist: cbor2<7,>=6.1.3
|
|
27
27
|
Requires-Dist: cryptography>=43.0
|
|
28
28
|
Requires-Dist: gitpython>=3.1.40
|
|
29
|
-
Requires-Dist: matrx-utils>=2.0.
|
|
29
|
+
Requires-Dist: matrx-utils>=2.0.53
|
|
30
30
|
Requires-Dist: psycopg-pool>=3.2.5
|
|
31
31
|
Requires-Dist: psycopg[binary]>=3.2.5
|
|
32
32
|
Requires-Dist: python-dotenv>=1.0
|
|
@@ -72,7 +72,13 @@ from .core.diagnostics import (
|
|
|
72
72
|
long_transactions,
|
|
73
73
|
long_transactions_sql,
|
|
74
74
|
)
|
|
75
|
-
from .core.introspection import
|
|
75
|
+
from .core.introspection import (
|
|
76
|
+
introspect_column_shapes,
|
|
77
|
+
introspect_columns,
|
|
78
|
+
introspect_function_privileges,
|
|
79
|
+
introspect_rls_policies,
|
|
80
|
+
schema_exists,
|
|
81
|
+
)
|
|
76
82
|
from .core.one_database import OneDatabaseReport, PoolIdentity, audit_one_database
|
|
77
83
|
from .core.listen import notify, ping
|
|
78
84
|
from .core.tls import resolve_asyncpg_ssl, resolve_asyncpg_ssl_from_dsn
|
|
@@ -472,7 +478,9 @@ __all__ = [
|
|
|
472
478
|
"audit_one_database",
|
|
473
479
|
"OneDatabaseReport",
|
|
474
480
|
"PoolIdentity",
|
|
481
|
+
"introspect_column_shapes",
|
|
475
482
|
"introspect_columns",
|
|
483
|
+
"introspect_function_privileges",
|
|
476
484
|
"introspect_lock_waits",
|
|
477
485
|
"introspect_rls_policies",
|
|
478
486
|
"schema_exists",
|
|
@@ -113,3 +113,43 @@ SELECT c.relname AS table_name, a.attname AS column_name
|
|
|
113
113
|
AND NOT a.attisdropped
|
|
114
114
|
AND c.relkind = ANY ('{r,p,v,m,f}')
|
|
115
115
|
"""
|
|
116
|
+
|
|
117
|
+
# The same catalog read, plus the two facts a drift guard actually argues about:
|
|
118
|
+
# whether a column may be empty, and what Postgres puts there when a writer says
|
|
119
|
+
# nothing. A NOT NULL column with a DEFAULT manufactures a value that reads,
|
|
120
|
+
# downstream, exactly like something a person chose — so "is there a default"
|
|
121
|
+
# is a question the ORM must be able to answer without a host hand-rolling SQL.
|
|
122
|
+
COLUMN_SHAPES_FOR_TABLE_SQL = """
|
|
123
|
+
SELECT a.attname AS column_name,
|
|
124
|
+
NOT a.attnotnull AS is_nullable,
|
|
125
|
+
pg_get_expr(d.adbin, d.adrelid) AS column_default
|
|
126
|
+
FROM pg_attribute a
|
|
127
|
+
JOIN pg_class c ON c.oid = a.attrelid
|
|
128
|
+
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
129
|
+
LEFT JOIN pg_attrdef d ON d.adrelid = a.attrelid AND d.adnum = a.attnum
|
|
130
|
+
WHERE n.nspname = $1
|
|
131
|
+
AND c.relname = $2
|
|
132
|
+
AND a.attnum > 0
|
|
133
|
+
AND NOT a.attisdropped
|
|
134
|
+
AND c.relkind = ANY ('{r,p,v,m,f}')
|
|
135
|
+
"""
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
# "Who may CALL this function?" is the other half of the definer question. A
|
|
139
|
+
# SECURITY DEFINER function runs as the owner with BYPASSRLS, so the set of
|
|
140
|
+
# roles holding EXECUTE on it IS its access control — and a grant that quietly
|
|
141
|
+
# includes `anon` or `authenticated` hands a browser the owner's privileges.
|
|
142
|
+
# Like COLUMN_SHAPES_FOR_TABLE_SQL, this is a catalog question with no Model to
|
|
143
|
+
# bind it to, so the ORM answers it rather than every guard hand-rolling SQL.
|
|
144
|
+
FUNCTION_PRIVILEGES_SQL = """
|
|
145
|
+
SELECT p.oid::regprocedure::text AS signature,
|
|
146
|
+
p.prosecdef AS is_security_definer,
|
|
147
|
+
role_name AS role,
|
|
148
|
+
has_function_privilege(role_name, p.oid, 'EXECUTE') AS can_execute
|
|
149
|
+
FROM pg_proc p
|
|
150
|
+
JOIN pg_namespace n ON n.oid = p.pronamespace
|
|
151
|
+
CROSS JOIN unnest($3::text[]) AS role_name
|
|
152
|
+
WHERE n.nspname = $1
|
|
153
|
+
AND p.proname = $2
|
|
154
|
+
AND EXISTS (SELECT 1 FROM pg_roles r WHERE r.rolname = role_name)
|
|
155
|
+
"""
|
|
@@ -17,11 +17,21 @@ from __future__ import annotations
|
|
|
17
17
|
|
|
18
18
|
from typing import Any
|
|
19
19
|
|
|
20
|
-
from matrx_orm.catalog_sql import
|
|
20
|
+
from matrx_orm.catalog_sql import (
|
|
21
|
+
COLUMN_SHAPES_FOR_TABLE_SQL,
|
|
22
|
+
COLUMNS_FOR_TABLES_SQL,
|
|
23
|
+
FUNCTION_PRIVILEGES_SQL,
|
|
24
|
+
SCHEMA_EXISTS_SQL,
|
|
25
|
+
)
|
|
21
26
|
from matrx_orm.core.async_db_manager import AsyncDatabaseManager
|
|
22
27
|
from matrx_orm.core.transaction import get_active_connection
|
|
23
28
|
|
|
24
|
-
__all__ = [
|
|
29
|
+
__all__ = [
|
|
30
|
+
"introspect_function_privileges",
|
|
31
|
+
"introspect_rls_policies",
|
|
32
|
+
"introspect_columns",
|
|
33
|
+
"schema_exists",
|
|
34
|
+
]
|
|
25
35
|
|
|
26
36
|
_RLS_AUDIT_SQL = """
|
|
27
37
|
select
|
|
@@ -77,6 +87,7 @@ async def introspect_rls_policies(
|
|
|
77
87
|
# we assume, present columns read as absent and the drift guard reports the code
|
|
78
88
|
# as wrong about the schema. See matrx_orm/catalog_sql.py.
|
|
79
89
|
_COLUMNS_SQL = COLUMNS_FOR_TABLES_SQL
|
|
90
|
+
_COLUMN_SHAPES_SQL = COLUMN_SHAPES_FOR_TABLE_SQL
|
|
80
91
|
|
|
81
92
|
|
|
82
93
|
async def introspect_columns(database: str, schema: str, tables: list[str]) -> dict[str, list[str]]:
|
|
@@ -120,3 +131,66 @@ async def schema_exists(database: str, schema: str) -> bool:
|
|
|
120
131
|
|
|
121
132
|
async with AsyncDatabaseManager.get_connection(database) as conn:
|
|
122
133
|
return bool(await conn.fetchval(sql, schema))
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
async def introspect_column_shapes(
|
|
137
|
+
database: str, schema: str, table: str
|
|
138
|
+
) -> dict[str, dict[str, object]]:
|
|
139
|
+
"""Live nullability and default per column — ``{column: {...}}``.
|
|
140
|
+
|
|
141
|
+
``introspect_columns`` answers "does this column exist"; this answers "what
|
|
142
|
+
will Postgres do when a writer says nothing about it". A NOT NULL column
|
|
143
|
+
with a DEFAULT does not leave a hole — it FILLS one, and every reader
|
|
144
|
+
downstream sees a value indistinguishable from a deliberate choice. On
|
|
145
|
+
2026-09-22 that shape on one column
|
|
146
|
+
(``communication.sms_notification_preferences.timezone``) made an entire
|
|
147
|
+
feature a no-op in production. A guard must be able to see it without
|
|
148
|
+
hand-rolling SQL.
|
|
149
|
+
|
|
150
|
+
Each value is ``{"is_nullable": bool, "column_default": str | None}``.
|
|
151
|
+
``schema``/``table`` are bound as parameters, never interpolated.
|
|
152
|
+
"""
|
|
153
|
+
rows = await AsyncDatabaseManager.execute_query(database, _COLUMN_SHAPES_SQL, schema, table)
|
|
154
|
+
return {
|
|
155
|
+
str(r["column_name"]): {
|
|
156
|
+
"is_nullable": bool(r["is_nullable"]),
|
|
157
|
+
"column_default": (
|
|
158
|
+
str(r["column_default"]) if r["column_default"] is not None else None
|
|
159
|
+
),
|
|
160
|
+
}
|
|
161
|
+
for r in rows
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
async def introspect_function_privileges(
|
|
166
|
+
database: str, schema: str, name: str, roles: list[str]
|
|
167
|
+
) -> list[dict[str, Any]]:
|
|
168
|
+
"""Which of ``roles`` can EXECUTE ``schema.name`` — one row per overload/role.
|
|
169
|
+
|
|
170
|
+
``introspect_column_shapes`` answers "what will Postgres do when a writer
|
|
171
|
+
says nothing"; this answers "who can call this door". A SECURITY DEFINER
|
|
172
|
+
function runs as its owner with BYPASSRLS, so the roles holding EXECUTE on
|
|
173
|
+
it ARE its access control: if ``anon`` or ``authenticated`` is among them,
|
|
174
|
+
a browser holds the owner's privileges. A guard has to be able to see that
|
|
175
|
+
without hand-rolling a catalog query, which is why this lives here.
|
|
176
|
+
|
|
177
|
+
Each row is ``{"signature": str, "is_security_definer": bool, "role": str,
|
|
178
|
+
"can_execute": bool}``. An overload that exists yields one row per role in
|
|
179
|
+
``roles``; a function that does not exist yields no rows at all, which is
|
|
180
|
+
how a caller tells "missing" from "locked". A name in ``roles`` that is not
|
|
181
|
+
a role on this server is skipped rather than raising — a server without a
|
|
182
|
+
``supabase_auth_admin`` is a different deployment, not a violation.
|
|
183
|
+
``schema``, ``name`` and ``roles`` are bound parameters, never interpolated.
|
|
184
|
+
"""
|
|
185
|
+
rows = await AsyncDatabaseManager.execute_query(
|
|
186
|
+
database, FUNCTION_PRIVILEGES_SQL, schema, name, list(roles)
|
|
187
|
+
)
|
|
188
|
+
return [
|
|
189
|
+
{
|
|
190
|
+
"signature": str(r["signature"]),
|
|
191
|
+
"is_security_definer": bool(r["is_security_definer"]),
|
|
192
|
+
"role": str(r["role"]),
|
|
193
|
+
"can_execute": bool(r["can_execute"]),
|
|
194
|
+
}
|
|
195
|
+
for r in rows
|
|
196
|
+
]
|
|
@@ -18,6 +18,7 @@ import json
|
|
|
18
18
|
import logging
|
|
19
19
|
import os
|
|
20
20
|
import re
|
|
21
|
+
import sys
|
|
21
22
|
import tempfile
|
|
22
23
|
import traceback as _tb
|
|
23
24
|
import uuid as _uuid
|
|
@@ -778,13 +779,50 @@ async def record_error(
|
|
|
778
779
|
color="yellow",
|
|
779
780
|
)
|
|
780
781
|
except Exception as capture_exc:
|
|
782
|
+
# 🚨 THE ANNOUNCEMENT MUST SURVIVE THE OUTAGE IT DESCRIBES (2026-09-21).
|
|
783
|
+
#
|
|
784
|
+
# Every field below used to ride ONLY in ``extra``, and the log MESSAGE
|
|
785
|
+
# was the bare token ``matrx_orm_system_error_capture_failed``. ``extra``
|
|
786
|
+
# is read by exactly one consumer — the app_log handler, which inserts
|
|
787
|
+
# into the SAME Postgres this capture just failed against. So the one
|
|
788
|
+
# class of failure that kills the capture (a lock wave, a stalled
|
|
789
|
+
# trigger, a pool starve) killed its own announcement too, and stdout
|
|
790
|
+
# got a token with no route, no request id and no exception.
|
|
791
|
+
#
|
|
792
|
+
# Verified in production: 2026-09-21 18:48–18:51 UTC, a 48 s
|
|
793
|
+
# ``platform.provision_restricted(...)`` held a relation lock; SIX
|
|
794
|
+
# ``POST /browser-manager/profiles`` requests answered 500
|
|
795
|
+
# LockContentionError; exactly ONE reached ``ops.system_error``
|
|
796
|
+
# (request_id 5c4f84d9f19a4503ba127f3d8228d420, 18:50:45). The other
|
|
797
|
+
# five left 27 bare ``matrx_orm_system_error_capture_failed`` tokens in
|
|
798
|
+
# CloudWatch beside ``[app_log] drain insert failed, dropped 200 row(s):
|
|
799
|
+
# <LockContentionError>`` — both durable lanes died together and the
|
|
800
|
+
# surviving line named nothing.
|
|
801
|
+
#
|
|
802
|
+
# So the message itself is now SELF-CONTAINED: whoever reads the plain
|
|
803
|
+
# log stream gets the kind, the route, the request id, the real
|
|
804
|
+
# exception and why the capture failed, with no DB and no structured
|
|
805
|
+
# sink required. ``extra`` is unchanged for the queryable lane.
|
|
806
|
+
summary = (
|
|
807
|
+
"matrx_orm_system_error_capture_failed "
|
|
808
|
+
f"kind={kind} error_type={resolved_error_type} "
|
|
809
|
+
f"route={route or '-'} request_id={request_id or '-'} "
|
|
810
|
+
f"user_id={user_id or '-'} "
|
|
811
|
+
f"capture_failed_with={type(capture_exc).__name__}: "
|
|
812
|
+
f"{_capture_failure_diagnostic(capture_exc)} | "
|
|
813
|
+
f"UNRECORDED FAILURE: {_capture_failure_diagnostic(resolved_error_text)}"
|
|
814
|
+
)
|
|
815
|
+
# stdout FIRST and unconditionally: the logging tree can be
|
|
816
|
+
# misconfigured, filtered, or draining into the dead database, and this
|
|
817
|
+
# is the last place the failure can still be seen.
|
|
818
|
+
print(f"[matrx_orm.session.fallback] {summary}", file=sys.stdout, flush=True)
|
|
781
819
|
# ``vcprint(..., color="red")`` is itself a logger call. Emitting it
|
|
782
820
|
# beside this structured event made ONE failed capture appear as TWO
|
|
783
821
|
# unrelated ERROR families in ``app_log``. Keep one ERROR so the
|
|
784
822
|
# durable log has an exact, queryable incident without concealing the
|
|
785
823
|
# original failure or the bounded-capture refusal.
|
|
786
824
|
logger.error(
|
|
787
|
-
|
|
825
|
+
summary,
|
|
788
826
|
extra={
|
|
789
827
|
"matrx_system_error": {
|
|
790
828
|
"kind": kind,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "matrx-orm"
|
|
3
|
-
version = "3.1.
|
|
3
|
+
version = "3.1.178"
|
|
4
4
|
description = "Async-first PostgreSQL ORM with bidirectional migrations, schema introspection, many-to-many relationships, and built-in state caching"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = { text = "MIT" }
|
|
@@ -41,7 +41,7 @@ dependencies = [
|
|
|
41
41
|
"gitpython>=3.1.40",
|
|
42
42
|
"psycopg[binary]>=3.2.5",
|
|
43
43
|
"psycopg-pool>=3.2.5",
|
|
44
|
-
"matrx-utils>=2.0.
|
|
44
|
+
"matrx-utils>=2.0.53",
|
|
45
45
|
"pyyaml>=6.0.3",
|
|
46
46
|
"python-dotenv>=1.0",
|
|
47
47
|
"cryptography>=43.0",
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"""A capture that cannot reach the database still SAYS what it lost.
|
|
2
|
+
|
|
3
|
+
SUT: the failure branch of :func:`matrx_orm.session.fallback.record_error`.
|
|
4
|
+
|
|
5
|
+
🚨 THE INCIDENT THIS GUARDS (production, 2026-09-21 18:48–18:51 UTC). A single
|
|
6
|
+
48-second ``platform.provision_restricted(...)`` held a relation lock. SIX
|
|
7
|
+
``POST /browser-manager/profiles`` requests answered
|
|
8
|
+
``500 Internal server error: LockContentionError``. Exactly ONE of them reached
|
|
9
|
+
``ops.system_error`` (request_id 5c4f84d9f19a4503ba127f3d8228d420, 18:50:45) —
|
|
10
|
+
the one that happened to fall in a gap between lock waves. The other five were
|
|
11
|
+
silent, because:
|
|
12
|
+
|
|
13
|
+
1. ``record_error``'s INSERT goes to the SAME Postgres that is stalled, so it
|
|
14
|
+
fails exactly when the failure most needs recording; and
|
|
15
|
+
2. its fallback announcement was ``logger.error("matrx_orm_system_error_...")``
|
|
16
|
+
with EVERY field — route, request id, the real exception — riding in
|
|
17
|
+
``extra``, which only the app_log handler reads, and that handler inserts
|
|
18
|
+
into the same dead database (``[app_log] drain insert failed, dropped 200
|
|
19
|
+
row(s): <LockContentionError>``).
|
|
20
|
+
|
|
21
|
+
Both durable lanes died together and the only line that survived named nothing.
|
|
22
|
+
|
|
23
|
+
Breaks this test: moving any of kind / route / request id / the original error
|
|
24
|
+
text back out of the log MESSAGE and into ``extra`` alone; dropping the direct
|
|
25
|
+
stdout write; or making the announcement depend on the logging tree being
|
|
26
|
+
alive.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
from __future__ import annotations
|
|
30
|
+
|
|
31
|
+
import logging
|
|
32
|
+
|
|
33
|
+
import pytest
|
|
34
|
+
from matrx_orm.session import fallback
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class _StalledDatabase(Exception):
|
|
38
|
+
"""Stands in for the lock-timeout the capture INSERT hit in production."""
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
# Two real jobs on the platform, so no single hard-coded announcement can
|
|
42
|
+
# satisfy both (forcing-function-tests §3).
|
|
43
|
+
_INCIDENTS = [
|
|
44
|
+
pytest.param(
|
|
45
|
+
# All Green Recycling: an operator starting a cloud browser to file a
|
|
46
|
+
# pickup manifest with the county portal.
|
|
47
|
+
{
|
|
48
|
+
"kind": "request_crash",
|
|
49
|
+
"route": "POST /browser-manager/profiles",
|
|
50
|
+
"request_id": "5c4f84d9f19a4503ba127f3d8228d420",
|
|
51
|
+
"user_id": "9f1c7b64-2b3a-4d51-8d9c-1e2f3a4b5c6d",
|
|
52
|
+
"error_type": "LockContentionError",
|
|
53
|
+
"error_text": "LockContentionError: canceling statement due to lock timeout",
|
|
54
|
+
"capture_failure": "canceling statement due to lock timeout on ops.system_error",
|
|
55
|
+
},
|
|
56
|
+
id="all_green_recycling_browser_profile",
|
|
57
|
+
),
|
|
58
|
+
pytest.param(
|
|
59
|
+
# Harbor Dental: a front-desk save on the new-patient intake form.
|
|
60
|
+
{
|
|
61
|
+
"kind": "request_crash",
|
|
62
|
+
"route": "POST /forms/new-patient-intake/submissions",
|
|
63
|
+
"request_id": "a71e0d3c88f2427b93cc10b4d6e5f207",
|
|
64
|
+
"user_id": "3c8d2e17-90ab-4f66-b2e1-77d0c4a95b38",
|
|
65
|
+
"error_type": "DeadlockDetectedError",
|
|
66
|
+
"error_text": "DeadlockDetectedError: deadlock detected on intake_submission",
|
|
67
|
+
"capture_failure": "pool acquire timed out after 2.0s",
|
|
68
|
+
},
|
|
69
|
+
id="harbor_dental_intake_submission",
|
|
70
|
+
),
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _fail_the_capture(monkeypatch: pytest.MonkeyPatch, reason: str) -> None:
|
|
75
|
+
async def _boom(**_kwargs):
|
|
76
|
+
raise _StalledDatabase(reason)
|
|
77
|
+
|
|
78
|
+
monkeypatch.setattr(fallback, "_insert_error", _boom)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@pytest.mark.parametrize("incident", _INCIDENTS)
|
|
82
|
+
@pytest.mark.asyncio
|
|
83
|
+
async def test_a_failed_capture_names_route_request_id_and_the_real_exception(
|
|
84
|
+
incident: dict[str, str],
|
|
85
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
86
|
+
capsys: pytest.CaptureFixture[str],
|
|
87
|
+
) -> None:
|
|
88
|
+
fallback.reset_capture_storm_state()
|
|
89
|
+
_fail_the_capture(monkeypatch, incident["capture_failure"])
|
|
90
|
+
|
|
91
|
+
await fallback.record_error(
|
|
92
|
+
_StalledDatabase(incident["error_text"]),
|
|
93
|
+
kind=incident["kind"],
|
|
94
|
+
error_type=incident["error_type"],
|
|
95
|
+
error_text=incident["error_text"],
|
|
96
|
+
request_id=incident["request_id"],
|
|
97
|
+
user_id=incident["user_id"],
|
|
98
|
+
route=incident["route"],
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
announced = capsys.readouterr().out
|
|
102
|
+
# Everything an operator needs to act, in ONE line, with no database and no
|
|
103
|
+
# structured sink required to read it.
|
|
104
|
+
assert incident["route"] in announced
|
|
105
|
+
assert incident["request_id"] in announced
|
|
106
|
+
assert incident["error_type"] in announced
|
|
107
|
+
assert incident["error_text"] in announced
|
|
108
|
+
assert incident["capture_failure"] in announced
|
|
109
|
+
assert "_StalledDatabase" in announced, "the capture's own failure must be named"
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
@pytest.mark.parametrize("incident", _INCIDENTS)
|
|
113
|
+
@pytest.mark.asyncio
|
|
114
|
+
async def test_the_announcement_survives_a_dead_logging_tree(
|
|
115
|
+
incident: dict[str, str],
|
|
116
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
117
|
+
capsys: pytest.CaptureFixture[str],
|
|
118
|
+
) -> None:
|
|
119
|
+
"""The one lane that outlives the outage must not go through logging.
|
|
120
|
+
|
|
121
|
+
In the incident the logging tree WAS the dead lane: its only durable
|
|
122
|
+
consumer inserted into the stalled database. An announcement that exists
|
|
123
|
+
only as a ``logger`` call is therefore no announcement at all.
|
|
124
|
+
"""
|
|
125
|
+
fallback.reset_capture_storm_state()
|
|
126
|
+
_fail_the_capture(monkeypatch, incident["capture_failure"])
|
|
127
|
+
|
|
128
|
+
logging.disable(logging.CRITICAL)
|
|
129
|
+
try:
|
|
130
|
+
await fallback.record_error(
|
|
131
|
+
_StalledDatabase(incident["error_text"]),
|
|
132
|
+
kind=incident["kind"],
|
|
133
|
+
error_type=incident["error_type"],
|
|
134
|
+
error_text=incident["error_text"],
|
|
135
|
+
request_id=incident["request_id"],
|
|
136
|
+
route=incident["route"],
|
|
137
|
+
)
|
|
138
|
+
finally:
|
|
139
|
+
logging.disable(logging.NOTSET)
|
|
140
|
+
|
|
141
|
+
announced = capsys.readouterr().out
|
|
142
|
+
assert incident["request_id"] in announced
|
|
143
|
+
assert incident["route"] in announced
|
|
144
|
+
assert incident["error_text"] in announced
|
|
@@ -93,7 +93,7 @@ async def test_capture_insert_is_bounded_before_it_touches_the_table(monkeypatch
|
|
|
93
93
|
def test_error_capture_with_user_but_no_org_stamps_system_org() -> None:
|
|
94
94
|
# A fresh capture connection has no request actor GUC. The user id alone
|
|
95
95
|
# cannot trigger a personal-org stamp and used to cause NOT NULL failure.
|
|
96
|
-
user_id = "
|
|
96
|
+
user_id = "22222222-3333-4444-8555-666666666666"
|
|
97
97
|
assert fallback._capture_org(None, user_id) == fallback.SYSTEM_ORGANIZATION_ID
|
|
98
98
|
org_id = "bfe0205b-24c6-4ae9-9262-2fe2169fe10d"
|
|
99
99
|
assert fallback._capture_org(org_id, user_id) == org_id
|
|
@@ -129,13 +129,25 @@ async def test_failed_capture_emits_one_structured_error_not_a_banner_twin(
|
|
|
129
129
|
context={"credential": capture_secret},
|
|
130
130
|
)
|
|
131
131
|
|
|
132
|
+
# 2026-09-21: the message is now SELF-CONTAINED (kind / route / request id /
|
|
133
|
+
# the real exception ride IN it, not only in ``extra``) because the app_log
|
|
134
|
+
# handler that reads ``extra`` inserts into the very database a failed
|
|
135
|
+
# capture means is unreachable. The invariant this guard was written for is
|
|
136
|
+
# unchanged and still asserted: ONE ERROR record, never a vcprint banner
|
|
137
|
+
# twin — so it matches the incident PREFIX instead of the whole line.
|
|
132
138
|
records = [
|
|
133
139
|
record
|
|
134
140
|
for record in caplog.records
|
|
135
141
|
if record.name == "matrx_orm.session.fallback"
|
|
136
|
-
and record.getMessage()
|
|
142
|
+
and record.getMessage().startswith("matrx_orm_system_error_capture_failed")
|
|
137
143
|
]
|
|
138
144
|
assert len(records) == 1
|
|
145
|
+
# The self-contained half: readable with no database and no structured sink.
|
|
146
|
+
message = records[0].getMessage()
|
|
147
|
+
assert "route=/api/cloud-files/changes" in message
|
|
148
|
+
assert "request_id=request-1" in message
|
|
149
|
+
assert "ValueError: shed" in message
|
|
150
|
+
assert capture_secret not in message
|
|
139
151
|
captured = records[0].matrx_system_error
|
|
140
152
|
assert captured == {
|
|
141
153
|
"kind": "cloud_file_change_feed_shed",
|
|
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
|
|
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
|