# AI Contract Platform V2 Phase 3 Slice 3.3 Governance And Soft Delete Implementation Date: 2026-06-30 ## Scope This slice implements the first governance and soft-delete migration for Contract API, following `project-docs/V2-phase3-schema-hardening-design-review-2026-06-30.md`. Completed: - Added Alembic migration `20260630_0021_add_governance_soft_delete_fields.py`. - Added governance columns to core business tables: `created_by`, `updated_by`, `deleted_at`. - Added missing `tenant_id` columns to `review_tasks`, `contract_search_documents`, and `approval_timeline_items`. - Added SQLAlchemy governance mixin and helper functions. - Added default `deleted_at is null` filtering for main read and write paths. - Added source-visibility filtering for unified search, so stale search documents do not expose soft-deleted contracts, files, or review tasks. - Added runtime schema check for governance columns. - Added regression tests for governance field writes and soft-delete visibility. Not included in this slice: - No public delete or restore API was added. - `contract_audit_events` remains immutable and is not soft-deleted. - No cleanup migration removes old fields. - No Phase 3 review-result structured tables were added. - No UI field derivation changes were made. ## Tables Covered Governed tables: - `contracts` - `contract_files` - `review_tasks` - `contract_search_documents` - `approval_timeline_items` - `approval_instances` - `approval_steps` - `approval_actions` - `template_lifecycle_events` - `legal_asset_lifecycle_events` - `legal_change_impact_scans` Tenant backfill tables: - `review_tasks` - `contract_search_documents` - `approval_timeline_items` ## Compatibility Strategy The migration is expand-only for runtime behavior: - New governance fields are nullable. - `tenant_id` is added with a default for compatibility, then backfilled from `contracts` where possible. - Existing API paths and response shapes are unchanged. - Repository/orchestration code now filters soft-deleted rows by default. - Search checks both search document visibility and source object visibility. ## Runtime Behavior Default hidden after `deleted_at` is set: - Contract list and detail. - Workspace summaries and pending tasks. - Contract files list/detail/preview/download. - Review task list, queue, claim, complete, fail paths. - Unified search results for contract, file, and review sources. - Template lifecycle event reads. - Legal asset lifecycle event reads. - Legal change impact scan history reads. Still intentionally retained: - Audit facts remain readable through audit integrity paths because they are immutable evidence. - Backups still serialize contract headers in the MVP backup implementation; backup semantics should be reviewed in a later retention/archive slice. ## Validation Passed locally: ```powershell python -m compileall -q yuqei-contract-platform/services/contract-api/src/yuqei_contract_api yuqei-contract-platform/services/contract-api/migrations python -m pytest yuqei-contract-platform/services/contract-api/tests -q ``` Passed local Alembic migration cycle on a temporary SQLite database: ```powershell python -m alembic -c alembic.ini upgrade head python -m alembic -c alembic.ini downgrade 20260630_0020 python -m alembic -c alembic.ini upgrade head ``` New focused regressions: - Governance fields are recorded for contract, file, review task, and timeline rows. - Soft-deleted contract/file/review task rows are hidden from default read paths. - Runtime schema check reports `governance_columns`. ## Next Step Authoritative V2 smoke PostgreSQL/Docker verification passed on `192.168.3.90`. Deploy command: ```powershell $env:SSHPASS='' powershell.exe -NoProfile -ExecutionPolicy Bypass -File yuqei-infra\scripts\deploy-v2-smoke-docker.ps1 -UseSshpass -AllowDirtyWorktree -KeepRemoteBackup -SkipSmoke ``` Deploy result: - Artifact: `project-docs/qa-artifacts/v2-smoke-docker-deploy/2026-06-30T13-48-10` - Script result: `V2_SMOKE_DOCKER_DEPLOY_PASS` - Compose project: `yuqei-contract-v2-smoke` - Contract API: `http://192.168.3.90:18100` healthy - Contract Web: `http://192.168.3.90:13000` healthy - AI Platform API: `http://192.168.3.90:18101` healthy - AI golden sample gate: passed, 90 samples / 6 suites / 16 checks Runtime schema check: ```text status=ok database=yuqei_contract alembic actual=20260630_0021 alembic expected_head=20260630_0021 core_tables checked_count=20 governance_columns checked_tables=11 governance_columns checked_columns=36 postgres_extensions=pg_trgm ``` DB and API smoke sampling: ```text tenant=smoke-governance-0021 contract_id=contract-ba1f38fd8889 file_id=file-7f0fa0003f7d review_id=review-0f4356dc06d8 files_total=2 review_tasks=1 workspace_tasks=3 search_total=6 contract created_by=Smoke Owner updated_by=Smoke Legal not_deleted=true file created_by=Smoke Uploader updated_by=Smoke Uploader not_deleted=true review_task created_by=Smoke Legal updated_by=contract-review-worker-smoke-1 not_deleted=true ``` Soft-delete visibility smoke sampling: ```text contract_id=contract-e7e688407798 file_id=file-17b71b117566 review_id=review-087c10767e0f detail_status_after_contract_soft_delete=404 files_total_after_file_soft_delete=0 review_tasks_after_soft_delete=0 search_total_after_contract_soft_delete=0 ``` Next step after Slice 3.3: - Continue Phase 3 with Slice 3.4 UI display field derivation design, or first add a small product-level delete/restore API design if business operations need to exercise `deleted_at` without direct DB updates. - Do not use `-ResetSmokeVolumes` for subsequent smoke upgrades unless the explicit goal is to rebuild the smoke database from scratch.