# SQLite retirement with user-data preservation MariaDB is the standard target. Explicit SQLite compatibility remains a transition path for existing installations and test coverage; its presence does not qualify MariaDB behavior. This is a staged plan, not authorization to access production, alter existing databases or delete artifacts. Owner-verified production baseline: `12.2.2-MariaDB-ubu2404`, currently obtained through floating `mariadb:latest`. Use the official pinned `12.2.2` image for the present isolated rehearsal. Retain the distinction between container image ID and registry manifest digest. A future 12.3 LTS upgrade needs its own migration rehearsal and approval; this plan does not imply that it has run. | Stage | Required evidence before advancing | | --- | --- | | 1. Inventory | Operator lists each installation, provider/version, DB location, data root, ownership and backup retention without publishing secrets. Distinguish synthetic fixtures from real user data; inventory database, attachments/CV artifacts, account-deletion tombstones, Data Protection keys and frozen/submitted archives. Record schema/migration state and row counts. | | 2. Preserve and export | Operator-approved consistent snapshot using the supported SQLite backup path, including committed WAL contents; verify restore on an isolated copy. Preserve original DB and recovery files. Review `deploy/sqlite_export.py` coverage against every current table/column and relationship; its partial export of important tables is not a complete migration. Extend transfer tooling only after that review. Produce protected export manifests/counts/checksums without logging personal data. | | 3. Rehearse MariaDB transfer | Import a copy into a newly isolated MariaDB database using the pinned supported version and normal EF startup. Validate every table/owner relationship, IDs, foreign keys, null/empty behavior, JSON/text/Unicode, timestamps, revisions/history, frozen packages, attachments and Identity projections against the source. Verify referential integrity and restore from the target backup. Rehearse fresh/repeated/populated startup and rollback to the preserved source; never connect both live writers during rehearsal. | | 4. Prove coverage parity | Map each retained SQLite test/helper scenario to actual MariaDB evidence: independent contexts, first-save races, stale writes, held readers/writers, atomic rollback/recovery, constraints, tenant denial, backup/restore and populated migration reconciliation. Include browser seeds that directly read/write SQLite and acceptance scripts; API-only SQLite tests and generated SQL do not prove this gate. Review dialect/type/index/collation differences. CI must fail rather than skip when the explicit MariaDB lane cannot connect. | | 5. Approve and cut over | Operator reviews data comparison, restore rehearsal, downtime/write-freeze plan, credentials, backups and rollback window; explicit approval precedes production action. Freeze writes, take final consistent source backup/export, validate transfer again, select MariaDB explicitly and run smoke checks with correct ownership. Preserve original SQLite read-only and retained keys/artifacts. Rollback after new target writes requires a reconciled reverse transfer; switching connections alone would lose those writes. | | 6. Contract after retention | Only after migration sign-off, observation window and coverage parity: retire active SQLite harness defaults/tools, then provider branches and direct/native packages with dependency review. Keep immutable historical EF migrations and historical evidence unless separately reviewed compatibility policy permits removal. Delete user-data copies only under an operator-approved retention policy with validated recoverability. | No stage is assumed complete from standardisation code alone. SQLite fixtures, packages and migration branches remain now. Existing partial export tools are preserved but cannot certify lossless transfer. MariaDB runtime qualification and production cutover are separate decisions.