docs(database): record MariaDB qualification and retirement gates
MariaDB persistence qualification / mariadb (push) Failing after 1m7s

This commit is contained in:
cesnimda
2026-10-24 01:52:03 +02:00
parent a2c9bda0d1
commit e75c4a4775
20 changed files with 14568 additions and 12 deletions
+11 -2
View File
@@ -6,7 +6,7 @@ Job Tracker (Jobjakt) is a self-hosted workspace for managing job applications,
- Next.js 16 / React 19 / TypeScript / MUI frontend
- ASP.NET Core 9 / EF Core 9 API
- SQLite by default; MariaDB/MySQL for server deployments
- MariaDB across development, integration testing, CI and production; SQLite retained only during staged retirement
- FastAPI document/AI service with optional Ollama, Gemini, or Groq CV routing
- Docker Compose + nginx; Gitea Actions deployment
@@ -22,7 +22,15 @@ docker compose -f docker-compose.yml -f docker-compose.dev.yml up --build
Open `http://localhost:3000`. The API is available at `http://localhost:5202` in local process development.
For local processes:
For local processes, start the separate persistent development database first:
```powershell
docker compose -f docker-compose.database.yml up -d --wait mariadb-dev
```
Then run the API/UI below. Development settings use only the loopback `jobtracker_dev` database;
startup applies the existing migrations. [MariaDB setup and qualification](docs/database/mariadb-standardisation/README.md)
documents the pinned server, disposable integration lane, credentials and data-preservation rules.
```powershell
cd JobTrackerApi
@@ -38,6 +46,7 @@ npm run dev
```powershell
dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --no-restore
python scripts/mariadb-integration.py
cd job-tracker-ui
npm run lint
npm test -- --runInBand
+6 -2
View File
@@ -1,10 +1,14 @@
# MariaDB production bootstrap
## What this app supports
- SQLite for local/dev fallback
- MariaDB/MySQL for production when configured with:
- MariaDB for local development, integration testing, CI and production; explicit SQLite is a temporary existing-data/legacy-test path, never an automatic fallback.
- MariaDB is configured with:
- `DATABASE_PROVIDER=mariadb`
- `JOBTRACKER_CONNECTION_STRING=...`
- `DATABASE_SERVER_VERSION=12.2.2` (owner-verified production version; do not upgrade production implicitly)
For isolated development and CI use the [current setup and qualification record](../docs/database/mariadb-standardisation/README.md).
The old production bootstrap below is an operator runbook, not authorization for this development task.
## 1. Create database and user in MariaDB
Run this against your MariaDB server/container:
+7 -2
View File
@@ -8,7 +8,7 @@ Last verified: 2026-09-11 against the current repository.
flowchart LR
Browser --> Nginx[nginx static frontend]
Nginx --> API[ASP.NET Core API]
API --> DB[(SQLite or MariaDB/MySQL)]
API --> DB[(MariaDB)]
API --> Data[Data root: uploads, CV artifacts, exports, keys]
API --> AI[FastAPI AI service]
AI --> Local[local transformers/Ollama]
@@ -25,13 +25,18 @@ The frontend is a client-rendered React application inside a Next.js static-expo
|---|---|
| UI | Next.js 16, React 19, TypeScript 5.9, MUI 7, React Router 7 |
| API | ASP.NET Core 9, EF Core 9, ASP.NET Identity, JWT bearer, cookie sessions |
| Data | SQLite default; Pomelo MySQL/MariaDB provider for server deployments |
| Data | MariaDB standard for development/integration/CI/production; Pomelo 9.0.0, EF Core 9.0.14; explicit SQLite transition path retained pending safe retirement |
| AI | FastAPI, transformers/distilbart summarizer, OCR and document extractors, Ollama/Gemini/Groq CV routes |
| Runtime | Docker Compose, nginx 1.29-alpine, optional Ollama profile |
| Delivery | Gitea Actions → SSH deployment → Docker Compose |
Further detail: [frontend architecture](frontend.md), [backend architecture](backend.md), [data flow](data-flow.md), [integrations](integrations.md), [database schema](../database/schema.md), and [deployment](../infrastructure/deployment.md).
Database architecture decision updated 23 October 2026: production is owner-verified MariaDB
12.2.2, previously running floating `mariadb:latest`. The development/integration image is pinned
by version and verified registry manifest digest. [Standardisation record](../database/mariadb-standardisation/README.md)
separates actual runtime evidence, exact-image limits, remaining CI qualifications and staged SQLite retirement.
The older [architecture/current.md](current.md) path is retained as a compatibility pointer to this overview.
## Important boundaries
+3 -2
View File
@@ -6,8 +6,9 @@ This table covers the variables in `.env.example`, their Docker Compose mappings
| Variable | Required | Service | Secret | Default | Description |
|---|---|---|---|---|---|
| `DATABASE_PROVIDER` | Production | Compose/API | No | `sqlite` | `sqlite`, `mariadb`, or `mysql`; also selects the backup path. |
| `JOBTRACKER_CONNECTION_STRING` | Conditional | API | Yes | — | Server database connection string; ignored for SQLite. |
| `DATABASE_PROVIDER` | Production | Compose/API | No | `mariadb` | MariaDB standard; `mysql` alias retained, explicit `sqlite` only for transitional preservation. Unknown values fail closed. |
| `DATABASE_SERVER_VERSION` | No | Compose/API | No | `12.2.2` | Configured MariaDB `major.minor.patch`; registration never connects to auto-detect. |
| `JOBTRACKER_CONNECTION_STRING` | MariaDB | Compose/API | Yes | — | Required for MariaDB; missing configuration never falls back. Direct API equivalent: `ConnectionStrings__JobTracker`. Explicit SQLite can supply its existing path. |
| `APP_PUBLIC_BASE_URL` | Production | API/UI | No | — | Canonical external origin for links, callbacks, cookies, billing, and host validation. |
| `APP_VERSION` | No | API | No | — | Release/version metadata. |
| `APP_COMMIT_SHA` | No | API | No | — | Release commit metadata. |
+8 -2
View File
@@ -2,11 +2,17 @@
## Local development
SQLite and localhost defaults are supported. Run the API on 5202 and the UI on 3000, or use the development Compose override. Use seeded development credentials only from local settings and never reuse them in production.
MariaDB is the standard. Start the isolated persistent service in `docker-compose.database.yml`,
then run the API on 5202 and UI on 3000. Development settings use only loopback port33306 and
development-only credentials. See [MariaDB setup](../database/mariadb-standardisation/README.md).
Explicit SQLite selection is temporary for existing data and legacy test fixtures; it is never an automatic fallback.
## Tests
Jest, Playwright, xUnit, and the Python tests create isolated fixtures or temporary databases. They may set test-only variables such as `AI_SERVICE_SKIP_MODEL_LOAD`; they must not use a real database, mailbox, provider key, or production URL.
Jest, Playwright, xUnit and Python use isolated fixtures. The MariaDB integration lane uses an actual
disposable local server and creates only generated `jt_it_*` schemas. Never use an existing user or
production database, mailbox, provider key or production URL. Legacy SQLite/InMemory tests remain
explicitly labelled transition evidence and cannot substitute for MariaDB qualification.
## Production
+5 -1
View File
@@ -1,6 +1,10 @@
# Database
EF Core owns the current schema and migrations. SQLite is the local default; MariaDB/MySQL is supported for server deployments.
EF Core owns the schema/migrations. MariaDB is the standard application database for local
development, integration testing, CI and production. SQLite is an explicit temporary preservation
and legacy-test path pending staged retirement, not an alternate architecture.
- [MariaDB setup, RD21 qualification and retirement decisions](mariadb-standardisation/README.md)
- [Schema overview](schema.md)
- [Migrations and compatibility](migrations.md)
@@ -0,0 +1,83 @@
# MariaDB standardisation and RD21 qualification — 24 October 2026
RD21 is **resolved within the qualified MariaDB 12.2.2 save/concurrency scope**. The pinned official
Linux/amd64 image passes the actual Docker Compose persistence lane and a native browser/API recovery
journey with independent SQL reads. This supersedes the earlier addendum's missing-MariaDB blocker;
it does not reaccept all of Phase 9 or authorize release.
Baseline: `0d16a691e0249fe1264bf960de5e59eaa9b87fed`. Implementation checkpoint: `a2c9bda` on
`codex/rd21-mariadb-standardisation`. Main stays `5f835e54a9b74e6b71418422e28431b2af06453f`.
Raw runtime timestamps are UTC on 23 October; the owner's local report date is 24 October.
See [machine-readable validation](validation.json), [setup](README.md), and [runtime provenance](runtime-provenance.json).
## Separate decisions
| Gate | Verdict | Evidence or remaining blocker |
| --- | --- | --- |
| MariaDB 12.2.2 RD21 qualification | **PASS, bounded resolution** | 32 real-provider cases and one native browser journey; durable SQL state, transactions, rejected drafts, revision conflicts, recovery and tenant isolation. No production image equality claim. |
| MariaDB local development | **READY** | Pinned Compose image, healthy loopback service, separate persistent development volume, API startup applies 51 migrations; synthetic company survives database stop/start. |
| MariaDB CI/integration tests | **LOCAL READY; HOSTED UNVERIFIED** | Same fail-closed runner passes 32/32 using Docker, locked restore, isolated tmpfs and generated schemas. Workflow added; no successful hosted run claimed. Runner must provide Docker/Compose and .NET 9.0.317. |
| Historical fixture repairs | **PASS** | Three obsolete revision fixtures corrected without weakening assertions: diagnostics 18/18, volume 3/3. Stale writes now assert 409 and preservation of the winner/source/history. |
| SQLite retirement | **PLAN READY; REMOVAL NO-GO** | Complete baseline classification; defaults corrected. Remaining legacy coverage, full-fidelity transfer and preservation/restore gates must precede package/branch removal. |
| MariaDB 12.3 LTS migration | **INVESTIGATION READY; MIGRATION NO-GO** | Separate 12.3.3 rehearsal proposed; target image/driver certification, copied-data upgrade, backup/restore and rollback-after-writes remain untested. |
| Phase 9 reacceptance | **NO-GO** | RD21's provider blocker closes; wider manual/device/provider/security/output/release qualifications remain. Preservation remains 57 groups/33 routes, 56 PARTIAL and C11 DEFERRED. |
| Phase 10 readiness | **NO-GO** | Phase 9 not fully reaccepted; implementation explicitly prohibited. |
| Production readiness | **NO-GO** | No production qualification, infrastructure/backup-restore/release acceptance, deployed identity verification or reviewed pinning change. No production operation performed. |
## Verified scope and accounting
| Lane | Final distinct results | Limits |
| --- | --- | --- |
| Existing backend Release | 1,123 passed, 0 failed/skipped | Includes 14 configuration cases; retained SQLite/InMemory contracts do not count as MariaDB qualification. |
| MariaDB Docker Compose Release | 32 passed, 0 failed/skipped | Exact official 12.2.2 image; repeated run after bridge-network cleanup fix also passes. Repeats are not added to the count. |
| Native MariaDB browser | 1 passed | Real API, Chromium and SQL client against the same pinned Docker image; controlled network failure, not power-loss testing. |
| Corrected diagnostics | 18 passed | Explicit temporary SQLite browser lane; includes data/sustained fixtures. |
| Corrected volume fixtures | 3 passed | Explicit temporary SQLite lane; separate configuration. |
| Disposable-runner guards | 5 passed | Offline subprocess doubles, including setup/cleanup failure; not database proof. |
| Browser connection guards | 10 passed | Duplicate/alias keys, remote/existing schema and port mismatches rejected. |
| Static checks | Passed | Full frontend typecheck/lint and targeted browser/config TypeScript compilation. |
The initial real-server run failed: startup omitted three Identity columns. After schema repair,
the writer-lock test exposed dirty tracked entities when Pomelo wrapped error 1205 in
`InvalidOperationException`. Both defects were repaired and covered by real-provider assertions.
The browser then exposed an untranslatable import-history ordering query; ordering/limiting the
tenant-filtered entities before DTO projection repairs the MariaDB path. A later browser failure
used a stale Release binary after a Debug-only rebuild; the Release suite was rebuilt and the final
browser rerun passed, including import-history HTTP 200. These failures remain recorded in validation.
The final 32 cases exercise Career Profile and Job saves, stale revision rejection, missing/legacy
revision handling, atomic projection/history/job/event updates, independent reads after commits,
controlled concurrent writes and lock timeout recovery, repeated/populated startup, additive RD21
migration preservation and tenant isolation. They use actual InnoDB persistence with independent TCP
connections. Identity/summarizer doubles in controller fixtures do not replace database behavior;
the separate browser uses the real hosted API. They do not certify every application query or
historically restore data already overwritten before RD21. Existing creation/idempotency and
unrelated Phase 9 reliability qualifications remain outside this bounded repair.
Production's owner-reported server is `12.2.2-MariaDB-ubu2404`, but its reported configuration ID
differs from the verified official image config. Version parity is established; exact production
image/configuration/environment parity is not. The [image record](image-provenance.json) distinguishes
manifest and config digests. Locked provider versions are Pomelo 9.0.0, EF Core 9.0.14 and
MySqlConnector 2.4.0. Real 12.2.2 results establish this application's tested compatibility, despite
Pomelo's published older tested-server matrix; they do not certify 12.3.
## Preservation, review and operational limits
The [SQLite audit](SQLITE_AUDIT.md) classifies 1,602 baseline references in 415 tracked files:
1,169 required historical/preservation references, 420 temporary transition references and 13
obsolete defaults/guidance references. The obsolete active defaults/guidance were corrected;
SQLite dependencies, compatibility branches, historical migrations and user data remain preserved.
The three fixture changes have exact before/after deltas in [fixture-delta.json](fixture-delta.json).
Previous acceptance counts and reports remain historical, not silently rewritten.
Independent review found and verified fixes for duplicate connection keys bypassing the browser's
database guard, server reuse and missing actual version verification. The generated test service
now uses the existing bridge network, avoiding leaked per-run networks. Final review has no further
actionable findings. Docker's local stale socket issue was also resolved without resetting Docker
or deleting its persistent data; see [local recovery](DOCKER_LOCAL_RECOVERY.md).
The intended final architecture is **MariaDB only** for application development, integration,
CI and production. Keep 12.2.2 pinned for the present qualification; rehearse the supported 12.3 LTS
path separately using [the migration gates](MARIADB_12_3_PLAN.md). Retire SQLite in the documented
stages after equivalent coverage and verified preservation, not by deleting existing databases.
No main merge, deployment, production connection/container change or Phase 10 implementation occurred.
@@ -0,0 +1,35 @@
# Local Docker Desktop socket recovery — 24 October 2026
The reported `initializing Inference manager ... dockerInference ... file cannot be accessed`
startup failure was reproduced locally. The listener path was a zero-byte Windows reparse-point
socket; removing/moving that leaf failed with Windows error 1920. After preserving its runtime
directory, startup advanced to the same problem with the Secrets Engine's `engine.sock`.
A subsequent failed startup had left a new inference socket, so both socket-only directories
needed to be preserved together after stopping the task-started backend process tree.
The successful attempt retained these directories as archives and created fresh originals:
- `%LOCALAPPDATA%\Docker\run.stale-20261023-rd21`
- `%LOCALAPPDATA%\docker-secrets-engine.stale-20261024-rd21`
- `%LOCALAPPDATA%\Docker\run.stale-20261024-rd21-4e4339f5`
- `%LOCALAPPDATA%\docker-secrets-engine.stale-20261024-rd21-805dc9e6`
Each directory's contents were inspected first: only zero-byte socket reparse points, no ordinary
files or subdirectories. No Docker data directory, image, user container, volume, WSL disk or
production resource was removed or reset. Docker then started successfully: local context
`desktop-linux`, engine `29.7.2`; health checks, pinned MariaDB containers, 32 integration cases and
the native browser recovery case passed. Docker's own updater downloaded an available installer;
this task did not install it or request a Docker upgrade.
Do not blindly repeat directory moves while Docker is running, archive broader Docker directories,
or perform a factory reset. If this recurs, capture the exact startup error, stop only the affected
local Desktop process tree, verify that each named path still contains only disposable sockets,
and preserve it before replacement. Persistent data or unclear contents need separate review.
Before Docker recovered, an initial task-owned image extraction filled Docker Desktop's small
bootstrap filesystem. The failed extraction was removed from its verified task-specific temporary
directory and free space restored. The successful fallback used a private 2GB tmpfs and mount
namespace under WSL; it passed the same real MariaDB tests but was labelled separately from Docker
execution. That fallback server was gracefully shut down before retrying Desktop. No preexisting
database or user data was deleted. Raw logs and before/after metadata remain under the ignored
`.artifacts/mariadb/` directory.
@@ -0,0 +1,56 @@
# MariaDB 12.3 LTS migration investigation
Research checked against official sources on 2026-10-23. Recommendation: qualify **12.3.3 LTS** as the next server target in a separate staging change, then seek operator approval for production cutover. The present standardisation work aligns with the owner-verified deployed **12.2.2-MariaDB-ubu2404**; no 12.3 runtime, upgraded production copy, restore rehearsal or deployment is claimed here.
## Release and support evidence
| Question | Verified finding |
| --- | --- |
| Current stable 12.3 patch | The Foundation's [release inventory](https://mariadb.org/mariadb/all-releases/) lists 12.3.3 Stable in August 2026. The [Q3 maintenance announcement](https://mariadb.org/mariadb-server-12-3-11-8-11-4-and-10-11-q3-2026-maintenance-releases-and-goodbye-10-6/) confirms 12.3.3. Official pages differ on the exact August day, so this plan relies on the version and month. Recheck for a newer stable patch when scheduling rehearsal. |
| First GA | The [12.3 LTS announcement](https://mariadb.org/mariadb-server-12-3-lts-released/) identifies 12.3.2 as the first GA in May 2026. Do not select the older RC referenced by stale release-note indexes. |
| Maintenance horizon | The Foundation's [maintenance policy and release table](https://mariadb.org/about/#maintenance-policy) list 12.3 Community maintenance through **12 June 2029**; Enterprise and Extended dates are TBC. Current policy provides Community LTS binaries for three years and critical/security source releases for two further years. This is not a promise of five years of Community binaries or a commercial support entitlement. |
Production currently uses floating `mariadb:latest` according to the owner. A subsequent pull can change the version independently of the application release. Before any planned upgrade, record and preserve the actual source image reference, platform, registry digest and server version; verify the proposed `docker.io/library/mariadb:12.3.3` exists for the deployment architecture, retrieve its official registry manifest/index digest and record the selected platform manifest. Use a verified `version@sha256:...` reference for rehearsal and eventual rollout. No 12.3 digest was retrieved in this investigation, so none is invented here. A container `.Image` configuration ID is not a registry manifest digest. Version/digest verification remains a prerequisite, including for the rollback image.
## Application and driver assessment
Repository inspection found `net9.0`, Pomelo **9.0.0**, EF Core Relational **9.0.14** and locked MySqlConnector **2.4.0** in `JobTrackerApi/JobTrackerApi.csproj` and `JobTrackerApi/packages.lock.json`. Pomelo's [9.0.0 release notes](https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MySql/releases/tag/9.0.0) allow EF Core 9.0.x, .NET 8+ and MySqlConnector 2.4.0+, but list MariaDB test coverage only through **11.6**, including 11.4/10.11 LTS. They do not certify 12.2 or 12.3 for this application.
The current [MySqlConnector compatibility table](https://mysqlconnector.net/#server-compatibility) lists MariaDB 12.x (12.3). That driver-level statement is encouraging; it does not prove that this locked older driver, Pomelo SQL generation, EF migration chain and application transaction behavior work together on 12.3. Keep the current dependency set for the first controlled comparison; upgrade a dependency separately only for a verified incompatibility or maintenance need, and rerun the same acceptance lane. Configure Pomelo's MariaDB server version to match the tested target; an old hardcoded version or successful connection is insufficient evidence.
The [12.3 announcement](https://mariadb.org/mariadb-server-12-3-lts-released/) specifically warns that `innodb_snapshot_isolation` now defaults to **ON**. Record actual source/target `transaction_isolation`, `innodb_snapshot_isolation`, SQL mode, charset and collation instead of assuming defaults. Exercise the production settings with controlled held-reader/writer and overlapping-write tests, transaction abort/recovery and stale-write contracts. Do not silently disable the new setting just to obtain a green test; any override needs a reason, a recorded configuration and its own qualification.
The present isolated12.2.2 Windows and official Ubuntu-image-filesystem runs both actually reported
`innodb_snapshot_isolation=ON` with `REPEATABLE-READ`; see [runtime provenance](runtime-provenance.json).
Do not assume a source value of OFF from the release announcement. Production's actual isolation,
SQL mode and collation were not inspected, and12.3 itself has not been exercised here.
## Rehearsal and migration gates
| Gate | Required proof before production approval |
| --- | --- |
| Freeze inputs | Preserve source image, application commit, configuration inventory, migration history and recovery files. Identify engines/plugins, authentication/TLS, collations and any replication/Galera topology; follow topology-specific guidance if present. Pin the proposed target image/platform and backup clients. |
| Prove recovery first | Restore a consistent **pre-upgrade 12.2.2** recovery set into a separate 12.2.2 instance. Verify application startup, counts, owner boundaries, current/history revisions, attachments, frozen packages, encrypted data/key availability and deletion-tombstone replay. Record elapsed restore time and approved recovery objectives. |
| Upgrade an isolated copy | Follow the official [cross-series upgrade guidance](https://mariadb.com/docs/server/server-management/install-and-upgrade-mariadb/upgrading/platform-specific-upgrade-guides/upgrading-on-linux/upgrading-between-major-mariadb-versions), including clean shutdown and engine/config review. Run and record [mariadb-upgrade](https://mariadb.com/docs/server/clients-and-utilities/deployment-tools/mariadb-upgrade) on the isolated upgraded copy with proper data-directory permissions; verify system tables and table checks. Do not substitute an EF migration for server-system upgrade. |
| Container entrypoint | Review the exact image's upgrade behavior before enabling [MARIADB_AUTO_UPGRADE](https://mariadb.com/docs/server/server-management/automated-mariadb-deployment-and-administration/docker-and-mariadb/mariadb-server-docker-official-image-environment-variables). Its automatic system-database backup is not a complete application recovery set. Never point the candidate image at the only original volume. |
| Application schema | Run the unchanged startup migration/reconciliation chain on fresh 12.3, the upgraded populated copy, and repeated startup. Compare schema/indexes/constraints and `__EFMigrationsHistory`; validate Unicode, JSON/text, timestamps, nulls, unique keys and foreign keys. Retain historical migrations; create a new reviewed migration only for an identified schema need. |
| Application behavior | Run real-server RD21 first-save/stale/overlap/rollback/held-reader-writer tests using independent contexts, tenant/security boundaries and related text writers, then affected browser workflows. Compare 12.2.2 and 12.3.3 using identical fixtures/settings; record exact versions, exits and persisted-state evidence. Confirm failures leave the context/database usable. SQLite and generated SQL remain separate evidence. |
| Target recovery and observation | Back up and restore the 12.3 candidate, validate full recovery again, measure representative query/lock/error behavior, and rehearse the rollback path below. Only then prepare an operator-reviewed maintenance window and production change. |
The generic upgrade documentation supports investigating a direct 12.2-to-12.3 upgrade; this is an inference about the rehearsal path, not a claim that the repository's populated database has been proven upgradeable. This investigation did not locate a dedicated 12.2-to-12.3 procedure. Recheck exact target release/configuration notes before execution.
## Backup/deployment gaps found by read-only inspection
`deploy/deploy.sh:350–365` prefers an arbitrary host `mariadb-dump`, falls back to host `mysqldump`, then uses **floating `mariadb:11`** for the container client. `deploy/deploy.sh:283` also uses that floating image for backup-directory ownership repair. A later migration implementation must select verified source-compatible dump/import tools and remove accidental client-version drift; this plan changes no deployment script.
Existing dump flags include `--single-transaction --routines --events --quick`. [mariadb-dump documentation](https://mariadb.com/docs/server/clients-and-utilities/backup-restore-and-import-clients/mariadb-dump) limits snapshot consistency to transactional engines and warns against concurrent DDL. It also documents dump-header incompatibility with older import clients. Inventory nontransactional tables and system users/grants/plugins/config separately; a dump of only the application database does not preserve the whole server. Schedule a write/DDL freeze and coordinate filesystem recovery artifacts for the cutover snapshot.
`verify_sql_backup` checks nonempty gzip, schema text and a dump trailer. Those are useful corruption checks, but an actual restore must prove usability. `docs/infrastructure/backups.md` already leaves current production restore qualification as an operator gate. Preserve application data, uploads/CV artifacts, Data Protection keys, tombstones and required configuration together. If physical backup is chosen, use matching server/backup versions, prepare with the same backup version, and restore into a separate stopped instance with an empty destination; see official [backup/restore guidance](https://mariadb.com/docs/mariadb-cloud/cloud-data-handling/backup-and-restore/mariadb-backup). Retain the original untouched source and protected backup manifests/checksums until the approved retention window ends.
## Rollback and approval
MariaDB explicitly states that [in-place downgrade is unsupported](https://mariadb.com/docs/server/server-management/install-and-upgrade-mariadb/downgrading-between-major-versions-of-mariadb). Do not start 12.2.2 against a data directory already upgraded by 12.3. Restore the verified pre-upgrade recovery set into a separate pinned 12.2.2 instance. Before target writes are admitted, this returns to the preserved source state. After target writes, restoring that snapshot would lose acknowledged work: either keep writes frozen through acceptance, or approve and rehearse a logical reverse-transfer/reconciliation strategy before cutover. Do not assume reverse replication or newer-feature SQL will work on the old server.
The automatic failure path in `deploy/deploy.sh:638–648` restores previous **backend/frontend images only**. It neither downgrades MariaDB nor restores database schema/data; startup EF changes can outlive application rollback. A database upgrade needs a separate recovery decision, operator runbook, write-freeze window, verified backup location and explicit acceptance of any recovery-point loss. Production access, restart, backup execution, server/schema migration and cutover require their own operator authorization.
**Decision:** recommend 12.3 LTS for staged qualification; production migration is **not ready**. Outstanding gates are official image/digest/platform verification, populated upgrade and server-upgrade checks, exact locked-stack/runtime compatibility, isolation/concurrency proof, complete source/target restore rehearsals, measured downtime/recovery objectives and operator approval. No runtime tests, builds, production operations, image changes or CI/deployment edits were performed for this investigation.
@@ -0,0 +1,45 @@
# MariaDB standardisation implementation plan
> Execute autonomously under the product owner's explicit instruction, using focused independent tasks and final review. No additional design approval or integration menu is required.
**Goal:** Make MariaDB the standard application database, provide reproducible local/CI persistence tests and qualify the existing RD21 repair on a real server.
**Baseline:** `0d16a691e0249fe1264bf960de5e59eaa9b87fed`, branch `codex/rd21-mariadb-standardisation`.
**Architecture:** Reuse Pomelo 9.0.0, EF Core 9.0.14 and the existing startup migration/reconciliation chain. Use a standalone local Compose project with separate persistent development and disposable integration storage. Primary production-version target is owner-verified **12.2.2-MariaDB-ubu2404**, currently running floating `mariadb:latest`. Official 12.2.2 Linux/amd64 manifest `sha256:42a27baaba3e8f3f0159d16381d5a7156661aa91dcc65f663cabb6a6f9503c00` and all image blobs were obtained and verified. Its config digest differs from the owner's running image ID; exact image equality is not claimed. Native 12.2.2 may supply separately labelled local evidence if Docker cannot start. Investigate 12.3.3 LTS as a future staging target; never upgrade production in this task.
**Constraints:** No main merge, production deployment/migration/database access, live service restart, Phase 10, historical-data deletion or alteration of existing migration history. Do not label SQLite/InMemory or generated SQL as MariaDB runtime evidence. Preserve auth/CSRF/tenant/document boundaries.
## Tasks and ownership
1. Coordinator: inspect current configuration, use verified official MariaDB 12.2.2 image/runtime pin, create standalone Compose development/test services, explicit provider configuration and startup tests. Never silently fall back to SQLite. Preserve explicit transitional SQLite access to existing data. Provide safe test setup/reset guardrails and local instructions.
2. Persistence implementer: own `JobTrackerApi.MariaDb.Tests/` only. Create an explicit real-MariaDB integration project, unique disposable database fixture using loopback-only development credentials, actual startup migration/reconciliation, independent contexts and persisted-state assertions. Missing/unreachable server fails this explicit lane, never skips. Cover Career/job revisions, first-save and overlapping writes, atomic rollback/failure recovery, related text writers, tenant isolation, legacy missing revisions and held readers/writers. Do not alter existing production migration files. Report provider inconsistencies with exact failures before fixes.
3. Audit/fixture implementer: own the three obsolete Phase 9 diagnostic/volume fixture sources and new SQLite audit/retirement documentation. Inventory all tracked SQLite references and classify required historical/preservation, temporary transition, or obsolete current guidance/defaults. Repair source revision seeds and stale-write expectations to the correct safety contract without deleting or weakening safety assertions. Keep historical acceptance reports unchanged. Propose staged retirement; no data/package/migration deletion.
4. Coordinator: wire the same integration command to CI without deployment, run isolated actual MariaDB qualification and complete backend/frontend/affected browser/static checks, fix verified provider inconsistencies at their owner, review final diff, update current architecture/setup guidance and repo TODO. Commit/push validated checkpoints and verify remote equality/clean tree/main unchanged. Record all nine owner-requested readiness decisions and remaining blockers.
## Verification and stopping conditions
- Capture actual server version, provider/EF versions, migration state and fresh/repeated/populated startup results.
- New tests fail before relevant fixes; pass with independent persisted-state assertions after fixes. Concurrency uses controlled synchronization rather than arbitrary sleeps/retries.
- Test resources must be newly generated `jt_it_*` databases on loopback with dedicated local test credentials. Never reuse development volumes/databases or connection secrets. Cleanup may target only databases created by that fixture/run; retain useful failure artifacts.
- No SQLite dependency removal until its replacement coverage and required data-preservation path exist. Full retirement may remain a staged follow-up.
- RD21 closure requires actual supported MariaDB concurrency, constraints and rollback evidence, plus existing client/browser proof. Other Phase 9/manual/provider/production gates remain independent.
- Review failure behavior for missing/unknown provider, unavailable server, retry/repeated initialization, SQL dialect/type/index differences, and accidental selection of a non-disposable database.
## Initial findings
Baseline default/fallback provider: SQLite. MariaDB options hardcoded `11.0.0`. Existing application dependencies: Pomelo `9.0.0`, EF Core `9.0.14`, SQLite `9.0.14`/native `2.1.12`. Historical migration chain includes dialect branches plus startup reconciliation. Repository did not record deployed patch; `deploy/deploy.sh` uses `mariadb:11`. Owner subsequently verified production12.2.2 and image configID `sha256:e0236fc6386e7eacd9359e59d0a078bd7aa0d18280d36d13061121bedeaee903`; this is NOT a pullable registry digest. The initial11.4 candidate/archive was abandoned before runtime tests and provides no qualification. Docker Desktop initially failed creating its inference-manager Unix listener. That local failure was subsequently resolved by preserving verified socket-only runtime directories; see [recovery](DOCKER_LOCAL_RECOVERY.md). No factory reset or production service operation occurred.
## Completed qualification checkpoint
- [x] Official pinned12.2.2 Docker image obtained, verified and executed.
- [x] Persistent development service, actual API migrations and row preservation across restart.
- [x] Reproducible disposable integration runner32/32 and CI workflow (hosted execution unverified).
- [x] Provider defects reproduced and minimally repaired; current Release backend1123/1123.
- [x] Native MariaDB browser recovery1/1; canonical target guards10/10; runner guards5/5.
- [x] Three obsolete fixtures repaired; diagnostics18/18 and volume3/3.
- [x] Complete baseline SQLite classification, staged preservation-first retirement and12.3LTS investigation.
- [x] Independent review findings resolved; acceptance and explicit remaining gates recorded.
Publication is on the dedicated development branch only. The [acceptance record](ACCEPTANCE.md)
contains the nine separate decisions; wider Phase9, Phase10 and production remain NO-GO.
@@ -0,0 +1,157 @@
# MariaDB standardisation and RD21 qualification
MariaDB is the intended application database for development, integration testing, CI and
production. SQLite remains only as an explicit, temporary preservation/legacy-test path. Missing
MariaDB configuration and unknown provider names fail closed; they never select SQLite.
This work starts at RD21 repair `0d16a691e0249fe1264bf960de5e59eaa9b87fed` on
`codex/rd21-mariadb-standardisation`. The [final acceptance record](ACCEPTANCE.md) separates
runtime, Docker/CI, retirement and release decisions. No main merge or production operation is authorized.
## Version and image identity
The product owner verified production **12.2.2-MariaDB-ubu2404**, using floating `mariadb:latest`,
with running image configuration ID
`sha256:e0236fc6386e7eacd9359e59d0a078bd7aa0d18280d36d13061121bedeaee903`.
This is not a pullable registry manifest digest. The repository previously advertised11.0.0 and
used a floating11 client image; those facts did not identify production.
The standalone Compose database uses the official **Linux/amd64** image:
```text
mariadb:12.2.2@sha256:42a27baaba3e8f3f0159d16381d5a7156661aa91dcc65f663cabb6a6f9503c00
```
The manifest, configuration and every image layer were downloaded from the official registry and
SHA256/size verified. [Image provenance](image-provenance.json) records the corresponding config
digest `sha256:dec309ca7f95ab98e85fcf56acb9c4f05da59125c09d9e60a521f15f12643d7b` and Ubuntu24.04
package version. It differs from production's reported image ID: same server patch is not exact
production image equality. Other architectures need their own verified official manifest; do not
reuse this amd64 digest while claiming another platform.
Use explicit version plus verified digest for a future operator-reviewed production change.
`latest` can move independently of application releases. This task does not change, pull into,
restart, upgrade, downgrade or recreate production. [12.3 LTS investigation](MARIADB_12_3_PLAN.md)
recommends a separate pinned12.3.3 rehearsal, including isolation, driver and recovery gates.
## Persistent local development
Prerequisites: working local Docker engine/Compose, pinned .NET9.0.317 and existing frontend dependencies.
From the repository root:
```powershell
rtk proxy docker compose -f docker-compose.database.yml up -d --wait mariadb-dev
rtk proxy C:/Users/Connor/.dotnet/dotnet.exe run --project JobTrackerApi/JobTrackerApi.csproj
```
On other hosts use the installed pinned `dotnet`. The database is loopback-only port33306,
database/user `jobtracker_dev`, development password `jt-development-only`. The root password
`jt-development-root-only` belongs only to this standalone development service. The API's
Development settings match these values; authentication settings remain separately required.
Use local user secrets for non-placeholder auth/admin configuration. Do not copy these credentials
or volumes to production. Environment overrides such as `ConnectionStrings__JobTracker` take precedence.
The standalone Compose project `jobtracker-local` owns the persistent `mariadb_dev` volume. It is
separate from the production Compose file, networks, `.env` and application data volumes. A fresh
database is initialized by the API's existing migration/reconciliation startup. Repeated/populated
startup must preserve rows. Do not run `down -v` to resolve a schema problem, delete the dev volume,
or point tests at an existing user database. Stop the service without removing data if needed:
```powershell
rtk proxy docker compose -f docker-compose.database.yml stop mariadb-dev
```
There is intentionally no automatic destructive development reset. Export/backup and verify required
data before an independently authorized reset. Existing SQLite files are not opened, imported,
deleted or replaced by selecting the new default. To preserve/read an old database temporarily,
explicitly set `Database__Provider=sqlite` and its existing `ConnectionStrings__JobTracker` path;
do not confuse the Development MariaDB connection string with a SQLite connection string.
## Disposable MariaDB integration lane
```powershell
rtk proxy python scripts/test-mariadb-integration.py
rtk proxy python scripts/mariadb-integration.py
```
The second command creates a generated `jobtracker-it-<guid>` Compose project, allocates an
available loopback port and starts only `mariadb-test` with the pinned image and health checks.
Its data is private tmpfs, never the persistent development volume. The test administrator password
is separately `jt-integration-only`; no production credentials are needed. Each test creates a new
`jt_it_<guid>` schema and drops only that fixture's successfully-created schema. Tests initialize
the actual startup schema; they do not patch missing tables with `EnsureCreated`.
The runner performs locked restore and the complete `JobTrackerApi.MariaDb.Tests` project. Missing,
unreachable or version-mismatched databases fail the explicit lane, never skip or fall back.
Logs, native TRX and command/process outcomes go to `.artifacts/mariadb/integration/`. Cleanup
removes only the generated test service, without `down -v`; a cleanup error fails the runner.
To reset a manually started disposable service, first verify it is the test service with tmpfs
and contains no needed data, then remove only that service:
```powershell
rtk proxy docker compose -f docker-compose.database.yml --profile integration rm --stop --force mariadb-test
```
An isolated existing loopback test server can run the identical project:
```powershell
$env:JOBTRACKER_MARIADB_TEST_ADMIN='Server=127.0.0.1;Port=33307;User ID=root;Password=jt-integration-only;SslMode=None'
rtk proxy python scripts/mariadb-integration.py --existing-server --output .artifacts/mariadb/explicit-integration
```
The fixture rejects remote/non-TCP endpoints, requires exact12.2.2 by default and ignores any selected
admin database in favor of a new schema. Loopback is a guard, never permission to tunnel production
locally. Alternate `JOBTRACKER_MARIADB_TEST_VERSION` values label a separate compatibility run;
they cannot be used to claim12.2.2 parity. The CI workflow uses the Compose command, requires a Docker
daemon/Compose and preserves evidence. A checked-in workflow is not proof of a successful hosted run.
The ordinary backend project still contains explicitly transitional SQLite/InMemory unit/legacy
contracts. The ordinary browser fixture explicitly selects SQLite until its SQL seed/proof helpers
are ported. Those results remain useful but are never labelled MariaDB integration evidence.
## Native MariaDB browser recovery lane
Build the API in Release and the UI static export first. Install the official MariaDB command-line
client locally and set `JOBTRACKER_MARIADB_CLIENT` to its absolute executable path. This lane uses
the pinned test service, never the persistent development database. For a fresh run, use an unused
loopback port (33308 below) and a generated project/schema; preserve the resulting JSON report:
```powershell
$project='jobtracker-it-browser-'+[Guid]::NewGuid().ToString('N')
$db='jt_it_'+[Guid]::NewGuid().ToString('N')
$env:MARIADB_TEST_PORT='33308'
$env:JOBTRACKER_MARIADB_BROWSER_PORT='33308'
$env:JOBTRACKER_MARIADB_CLIENT=(Get-Command mariadb).Source
$env:MYSQL_PWD='jt-integration-only'
rtk proxy docker compose --project-name $project -f docker-compose.database.yml --profile integration up -d --wait mariadb-test
# Check each exit code before continuing. Only a successful CREATE establishes ownership.
& rtk proxy $env:JOBTRACKER_MARIADB_CLIENT --host=127.0.0.1 --port=33308 --user=root "--execute=CREATE DATABASE $db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
$env:JOBTRACKER_MARIADB_BROWSER_CONNECTION="Server=127.0.0.1;Port=33308;Database=$db;User ID=root;Password=jt-integration-only;SslMode=None"
$env:PLAYWRIGHT_STATIC_EXPORT='true'
$env:PLAYWRIGHT_PREBUILT_EXPORT='true'
$env:PLAYWRIGHT_JSON_OUTPUT_NAME='../.artifacts/mariadb/browser.json'
Push-Location job-tracker-ui
rtk proxy node node_modules/@playwright/test/cli.js test --config=playwright.mariadb.config.ts --reporter=list,json
Pop-Location
# Remove only this generated disposable container after retaining the test result and logs.
rtk proxy docker compose --project-name $project -f docker-compose.database.yml --profile integration rm --stop --force mariadb-test
```
The service's tmpfs discards only this run's test schemas when removed. Never substitute an existing
project/container or the dev service. The config accepts only the shown canonical connection format,
requires the independent SQL reader's port to match and refuses server reuse. The test checks actual
`SELECT VERSION()`, retains drafts across 409/network failures and checks durable SQL state before
and after reviewed retries, refresh and a new login. Run its boundary checks with
`rtk proxy node --test job-tracker-ui/scripts/mariadb-browser-connection.test.mjs`.
## Records
- [Implementation plan](PLAN.md), [actual validation](validation.json), [acceptance](ACCEPTANCE.md).
- [All baseline SQLite references/classification](sqlite-references.json), [audit summary](SQLITE_AUDIT.md).
- [Safe staged retirement](SQLITE_RETIREMENT.md), [three fixture deltas](fixture-delta.json).
- [12.3 LTS upgrade/recovery gates](MARIADB_12_3_PLAN.md).
Historical Phase9 acceptance and the previous RD21 addendum remain historical records. The three
active obsolete fixtures are intentionally corrected under the owner's new instruction; their old
failures, exact deltas and before/after hashes are retained. No SQLite package, compatibility schema
branch, database or migration history has been removed in this stage.
@@ -0,0 +1,29 @@
# Tracked SQLite reference audit
The machine-readable inventory is [sqlite-references.json](sqlite-references.json). It records every case-insensitive `sqlite` match in tracked text files at baseline `0d16a691e0249fe1264bf960de5e59eaa9b87fed`, with exact file, line and text, grouped classification reasons and per-file hashes. This fixed baseline keeps locations reproducible while the coordinator updates active configuration. It does not read untracked files, databases, exports or credentials. Binary files are excluded by `git grep -I`; case-insensitive matching includes `Sqlite`, `SQLitePCL`, `UseSqlite`, `sqlite3` and provider package names.
The owner subsequently verified deployed `12.2.2-MariaDB-ubu2404` from floating `mariadb:latest`; local qualification now targets the official `12.2.2` image. A container image ID is not a registry manifest digest. This supersedes the initial 11 LTS proposal; any later 12.3 LTS migration investigation is separate coordinator work.
| Classification | Meaning and disposition |
| --- | --- |
| `required_historical_or_data_preservation` | Historical evidence and immutable migrations; export/backup/restore paths; domain vocabulary. Preserve. A stored historical SQLite result is never MariaDB proof. |
| `temporary_transition` | Live provider branches/packages and SQLite-based tests, browser harnesses and local tools. Preserve until replacement coverage and user-data migration are qualified. |
| `obsolete` | Active default/fallback or current setup guidance contradicting MariaDB standardisation. Replace guidance/defaults; do not remove the preservation implementation. |
Concrete minimal coordinator changes at baseline:
| Location | Required change |
| --- | --- |
| `.env.example:7`, `docker-compose.yml:20`, `JobTrackerApi/Program.cs:100–105` | Select MariaDB explicitly; fail closed for missing/unknown provider instead of silently choosing SQLite. |
| `JobTrackerApi/Services/DatabaseBackupRunner.cs:31–36` | Match provider selection; never infer a SQLite backup path from an invalid provider. |
| `README.md:9`, `deploy/MARIADB.md:4` | Replace SQLite default/fallback wording with MariaDB local setup; retain migration/export instructions. |
| `docs/architecture/overview.md:28`, `docs/configuration/environment-variables.md:9`, `docs/configuration/environments.md:5`, `docs/database/README.md:3` | Align architecture and environment defaults; document explicit transitional SQLite access. |
| `docs/database/migrations.md:3` | Make actual MariaDB startup tests the standard provider qualification. Keep SQLite migration/WAL/backup compatibility facts. |
CI and Docker files were included in the same full tracked-tree scan. No baseline CI/Dockerfile text directly names SQLite; Compose does. SQLite also enters CI indirectly through the API test project and Playwright startup configuration. A new MariaDB CI lane must be explicit and fail on missing/unreachable MariaDB, while retained SQLite tests remain separate compatibility evidence.
Active compatibility surface includes API DI, startup reconciliation, dialect branches in controllers/services, EF provider/native packages, immutable migration branches, SQLite persisted tests, Python fixture seeds, browser configurations, deployment export/preview tools and acceptance-run local JWT lookup. Skill-tagger/summarizer strings are domain recognition vocabulary and should continue recognizing SQLite experience. The JSON reason groups distinguish these surfaces without duplicating thousands of source lines in prose.
The three obsolete revision fixtures are adapted under the owner's later explicit instruction. [fixture-delta.json](fixture-delta.json) records baseline/current hashes and exact unified source deltas. Historical acceptance reports and results stay unchanged, including the three previously recorded unversioned-seed failures. New execution evidence belongs to the coordinator and must be recorded separately.
Retirement gates: [SQLITE_RETIREMENT.md](SQLITE_RETIREMENT.md). No package, migration, database or historical evidence removal is part of this audit.
@@ -0,0 +1,16 @@
# 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.
File diff suppressed because one or more lines are too long
@@ -0,0 +1,63 @@
{
"verifiedUtc": "2026-10-23T18:50:02.746704+00:00",
"officialRepository": "docker.io/library/mariadb",
"tag": "12.2.2",
"platform": "linux/amd64",
"manifestDigest": "sha256:42a27baaba3e8f3f0159d16381d5a7156661aa91dcc65f663cabb6a6f9503c00",
"configDigest": "sha256:dec309ca7f95ab98e85fcf56acb9c4f05da59125c09d9e60a521f15f12643d7b",
"serverVersionEnvironment": [
"MARIADB_VERSION=1:12.2.2+maria~ubu2404"
],
"blobs": [
{
"digest": "sha256:dec309ca7f95ab98e85fcf56acb9c4f05da59125c09d9e60a521f15f12643d7b",
"bytes": 8764,
"verified": true
},
{
"digest": "sha256:b40150c1c2717d324cdb17278c8efdfa4dfcd2ffe083e976f0bcedf31115f081",
"bytes": 29732978,
"verified": true
},
{
"digest": "sha256:9ed055b717831a69ad03c2395a104ab8d08b033dfb39a7081aae4a77eff9960e",
"bytes": 1345,
"verified": true
},
{
"digest": "sha256:d94e08d2b3cae00353ca2773607a9af0a9c380a59cc24a5ae56060c96b5f024c",
"bytes": 5288087,
"verified": true
},
{
"digest": "sha256:d96030908d5aff54a5ff19b6ff91be5cae3cc515e4af20f9a4ab578d1222f5a3",
"bytes": 116,
"verified": true
},
{
"digest": "sha256:d0a6f83789fdbaf1fc1d0594f25bbf3bd8348ee9c19207b0403858f34215d030",
"bytes": 330,
"verified": true
},
{
"digest": "sha256:59bafaf3e6584086e742293aa889ba0a340d82575ad2e3cad30fbbae270f7f0b",
"bytes": 71776163,
"verified": true
},
{
"digest": "sha256:bf0bdffb5557248de03433ca4158608cba8d73bac2af7ca033d1bf72517c23d4",
"bytes": 4036,
"verified": true
},
{
"digest": "sha256:1ac5e0295552633b28897797cb63a7c6d7ee11f340cb888eca719ead5274c046",
"bytes": 8495,
"verified": true
}
],
"executed": true,
"ownerReportedProductionImageId": "sha256:e0236fc6386e7eacd9359e59d0a078bd7aa0d18280d36d13061121bedeaee903",
"productionImageEquality": false,
"limits": "Official image obtained, blobs verified, and Docker execution qualified. Production config ID differs; no production image equality or production environment qualification claimed.",
"execution": "Local Docker Compose Linux/amd64: 32 persistence cases passed; separately official image filesystem under WSL: 32 passed"
}
@@ -0,0 +1,58 @@
{
"recordedUtc": "2026-10-23T19:12:19.954085+00:00",
"settings": {
"nativeWindows": {
"version": "12.2.2-MariaDB",
"comment": "MariaDB Server",
"compiledOs": "Win64",
"transactionIsolation": "REPEATABLE-READ",
"snapshotIsolation": "ON",
"lowerCaseTableNames": 1,
"sqlMode": "STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION",
"characterSetServer": "utf8mb4",
"collationServer": "utf8mb4_uca1400_ai_ci",
"flushLogAtCommit": 1
},
"officialLinuxFilesystemUnderWsl": {
"version": "12.2.2-MariaDB-ubu2404",
"comment": "mariadb.org binary distribution",
"compiledOs": "debian-linux-gnu",
"transactionIsolation": "REPEATABLE-READ",
"snapshotIsolation": "ON",
"lowerCaseTableNames": 0,
"sqlMode": "STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION",
"characterSetServer": "utf8mb4",
"collationServer": "utf8mb4_uca1400_ai_ci",
"flushLogAtCommit": 1
},
"dockerCompose": {
"version": "12.2.2-MariaDB-ubu2404",
"transactionIsolation": "REPEATABLE-READ",
"snapshotIsolation": "ON",
"lowerCaseTableNames": 0,
"sqlMode": "STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION",
"characterSetServer": "utf8mb4",
"collationServer": "utf8mb4_unicode_ci",
"flushLogAtCommit": 1
}
},
"linuxExecution": "Verified official OCI layers reconstructed with whiteouts in private2GB tmpfs + mount namespace + chroot under WSL2; not Docker daemon/container execution.",
"productionSettings": "Only owner-provided version/image reference/config ID are known; no production connection or configuration inspection performed.",
"linuxBinaryHashes": "b61497f257d37ecc6424d7c8fe5cf0ae9449b1315368b496483c013cf9bcc032 /tmp/jt_rd21_ram_b01c41b94494448da8c493bf73fe69ab/rootfs/usr/sbin/mariadbd\n9c117da0e04b0983bafb9cd407c6ecc20d2c2dbff17d69ef714706c078b44820 /tmp/jt_rd21_ram_b01c41b94494448da8c493bf73fe69ab/rootfs/usr/local/bin/docker-entrypoint.sh\n",
"launcherSha256": "c56c7ebc18f4ab422bd1df3ca40185fdf4b5ec521ac6d091ebc10a691250d7ec",
"nativeArchiveSha256": "b34bb91f0dd4bd184f420288a12aace2b5fcb20734f2b880075c36150d4641c6",
"cleanupIncident": {
"initialDiskExtractionFailed": true,
"ownedDirectory": "/tmp/jt_rd21_5c7ce1af0d2c4e458b6407550b7b943d",
"removed": true,
"restoredFilesystem": "63MB used/54MB free; normal WSL invocation exits0",
"scope": "Only task-created temporary image extraction removed; no Docker reset, production operation or preexisting database deletion."
},
"dockerExecution": {
"engineVersion": "29.7.2",
"context": "desktop-linux",
"imageManifest": "sha256:42a27baaba3e8f3f0159d16381d5a7156661aa91dcc65f663cabb6a6f9503c00",
"persistenceCases": 32,
"nativeBrowserCases": 1
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,530 @@
{
"schemaVersion": 1,
"reportDate": "2026-10-24",
"recordedUtc": "2026-10-23T23:50:33.719141+00:00",
"baselineCommit": "0d16a691e0249fe1264bf960de5e59eaa9b87fed",
"implementationCommit": "a2c9bda0d11940e4ce1fb9fe5efa80b14ab6627b",
"branch": "codex/rd21-mariadb-standardisation",
"mainUnchanged": "5f835e54a9b74e6b71418422e28431b2af06453f",
"countsPolicy": "Distinct lanes only; focused/repeated/native/WSL/Docker runs overlap and are not added into one test total.",
"finalResults": {
"backend": {
"path": ".artifacts/mariadb/final-backend/backend.trx",
"sha256": "771884e589999f625ebbe2f5da7bbc31931d82db28363734f7fe8e88d53de0dc",
"bytes": 1616177,
"counters": {
"total": "1123",
"executed": "1123",
"passed": "1123",
"failed": "0",
"error": "0",
"timeout": "0",
"aborted": "0",
"inconclusive": "0",
"passedButRunAborted": "0",
"notRunnable": "0",
"notExecuted": "0",
"disconnected": "0",
"warning": "0",
"completed": "0",
"inProgress": "0",
"pending": "0"
}
},
"mariaDbCompose": {
"path": ".artifacts/mariadb/compose-final/mariadb.trx",
"sha256": "4e170909ddeddaed51fe52842a4a296e2ca4315b859b1db54a36e34ab8f720d6",
"bytes": 52575,
"counters": {
"total": "32",
"executed": "32",
"passed": "32",
"failed": "0",
"error": "0",
"timeout": "0",
"aborted": "0",
"inconclusive": "0",
"passedButRunAborted": "0",
"notRunnable": "0",
"notExecuted": "0",
"disconnected": "0",
"warning": "0",
"completed": "0",
"inProgress": "0",
"pending": "0"
},
"cases": [
{
"name": "JobTrackerApi.MariaDb.Tests.Rd21CareerPersistenceTests.Database_failure_after_deletion_restores_children_history_and_revision_and_clears_tracker",
"outcome": "Passed"
},
{
"name": "JobTrackerApi.MariaDb.Tests.Rd21CareerPersistenceTests.Empty_canonical_profile_preserves_legacy_raw_text_in_its_revision_zero_snapshot",
"outcome": "Passed"
},
{
"name": "JobTrackerApi.MariaDb.Tests.Rd21CareerPersistenceTests.Extraction_history_returns_only_the_ten_newest_owned_runs_with_exact_metadata",
"outcome": "Passed"
},
{
"name": "JobTrackerApi.MariaDb.Tests.Rd21CareerPersistenceTests.Reviewed_import_commits_or_rolls_back_canonical_projection_and_application_metadata(identitySucceeds: False)",
"outcome": "Passed"
},
{
"name": "JobTrackerApi.MariaDb.Tests.Rd21JobPersistenceTests.Related_text_save_cannot_silently_replace_a_newer_acknowledged_draft(coverLetter: True)",
"outcome": "Passed"
},
{
"name": "JobTrackerApi.MariaDb.Tests.StartupAndLockTests.Unsafe_or_missing_admin_configuration_fails_closed(connection: \"Server=192.0.2.1;Database=development;User ID=test\")",
"outcome": "Passed"
},
{
"name": "JobTrackerApi.MariaDb.Tests.Rd21CareerPersistenceTests.Stale_tracked_entity_is_guarded_at_database_update_and_stale_restore_is_rejected",
"outcome": "Passed"
},
{
"name": "JobTrackerApi.MariaDb.Tests.Rd21CareerPersistenceTests.Identity_failure_rolls_back_canonical_children_history_and_projection",
"outcome": "Passed"
},
{
"name": "JobTrackerApi.MariaDb.Tests.Rd21CareerPersistenceTests.Reviewed_import_commits_or_rolls_back_canonical_projection_and_application_metadata(identitySucceeds: True)",
"outcome": "Passed"
},
{
"name": "JobTrackerApi.MariaDb.Tests.StartupAndLockTests.Open_reader_does_not_block_independent_usage_and_identity_writes",
"outcome": "Passed"
},
{
"name": "JobTrackerApi.MariaDb.Tests.Rd21JobPersistenceTests.Structured_tailored_cv_snapshot_cannot_replace_a_newer_approved_document",
"outcome": "Passed"
},
{
"name": "JobTrackerApi.MariaDb.Tests.StartupAndLockTests.Rd21_additive_migration_preserves_existing_job_fields_and_backfills_revision_one",
"outcome": "Passed"
},
{
"name": "JobTrackerApi.MariaDb.Tests.Rd21JobPersistenceTests.Stale_or_unversioned_snapshot_cannot_erase_an_acknowledged_job_edit(omitRevision: False)",
"outcome": "Passed"
},
{
"name": "JobTrackerApi.MariaDb.Tests.Rd21CareerPersistenceTests.Competing_first_save_sources_have_one_acknowledged_winner",
"outcome": "Passed"
},
{
"name": "JobTrackerApi.MariaDb.Tests.StartupAndLockTests.Unsafe_or_missing_admin_configuration_fails_closed(connection: null)",
"outcome": "Passed"
},
{
"name": "JobTrackerApi.MariaDb.Tests.StartupAndLockTests.Admin_database_name_is_never_used_as_the_test_database",
"outcome": "Passed"
},
{
"name": "JobTrackerApi.MariaDb.Tests.Rd21CareerPersistenceTests.Replacement_restore_parse_and_import_fail_closed_without_valid_revision(expectedVersion: null)",
"outcome": "Passed"
},
{
"name": "JobTrackerApi.MariaDb.Tests.StartupAndLockTests.Unsafe_or_missing_admin_configuration_fails_closed(connection: \"Server=database.example.test;User ID=test;Password\"\u00b7\u00b7\u00b7)",
"outcome": "Passed"
},
{
"name": "JobTrackerApi.MariaDb.Tests.Rd21CareerPersistenceTests.Import_acceptance_requires_the_revision_shown_in_its_diff",
"outcome": "Passed"
},
{
"name": "JobTrackerApi.MariaDb.Tests.StartupAndLockTests.Fresh_repeated_and_populated_startup_preserve_owned_data_and_complete_migrations",
"outcome": "Passed"
},
{
"name": "JobTrackerApi.MariaDb.Tests.StartupAndLockTests.Full_startup_creates_every_table_and_column_required_by_the_persisted_EF_model",
"outcome": "Passed"
},
{
"name": "JobTrackerApi.MariaDb.Tests.Rd21CareerPersistenceTests.Parsing_commits_or_rolls_back_its_entire_import_application(identitySucceeds: False)",
"outcome": "Passed"
},
{
"name": "JobTrackerApi.MariaDb.Tests.Rd21CareerPersistenceTests.Independent_stale_snapshot_cannot_replace_acknowledged_career_edit",
"outcome": "Passed"
},
{
"name": "JobTrackerApi.MariaDb.Tests.Rd21JobPersistenceTests.Failed_job_transaction_cannot_acknowledge_or_leave_partial_data_for_a_later_save",
"outcome": "Passed"
},
{
"name": "JobTrackerApi.MariaDb.Tests.Rd21JobPersistenceTests.Held_writer_timeout_rolls_back_and_a_reviewed_save_recovers_after_release",
"outcome": "Passed"
},
{
"name": "JobTrackerApi.MariaDb.Tests.Rd21CareerPersistenceTests.Parsing_commits_or_rolls_back_its_entire_import_application(identitySucceeds: True)",
"outcome": "Passed"
},
{
"name": "JobTrackerApi.MariaDb.Tests.Rd21JobPersistenceTests.Stale_or_unversioned_snapshot_cannot_erase_an_acknowledged_job_edit(omitRevision: True)",
"outcome": "Passed"
},
{
"name": "JobTrackerApi.MariaDb.Tests.Rd21CareerPersistenceTests.Editor_snapshot_uses_relational_canonical_source_and_never_writes_during_read",
"outcome": "Passed"
},
{
"name": "JobTrackerApi.MariaDb.Tests.Rd21JobPersistenceTests.Related_text_save_cannot_silently_replace_a_newer_acknowledged_draft(coverLetter: False)",
"outcome": "Passed"
},
{
"name": "JobTrackerApi.MariaDb.Tests.Rd21CareerPersistenceTests.Replacement_restore_parse_and_import_fail_closed_without_valid_revision(expectedVersion: -1)",
"outcome": "Passed"
},
{
"name": "JobTrackerApi.MariaDb.Tests.Rd21JobPersistenceTests.Versioned_job_save_cannot_cross_tenant_job_or_company_ownership",
"outcome": "Passed"
},
{
"name": "JobTrackerApi.MariaDb.Tests.Rd21JobPersistenceTests.Write_that_overlaps_a_delayed_save_wins_without_partial_events_or_opportunity_changes",
"outcome": "Passed"
}
],
"runner": {
"startedUtc": "2026-10-23T23:45:02.659493+00:00",
"finishedUtc": "2026-10-23T23:45:35.164110+00:00",
"mode": "compose",
"expectedVersion": "12.2.2",
"project": "jobtracker-it-bab4e2d90fda49aca25affafc282e335",
"exitCode": 0,
"commands": [
{
"command": [
"docker",
"compose",
"--project-name",
"jobtracker-it-bab4e2d90fda49aca25affafc282e335",
"--project-directory",
"E:\\Storage\\Projects\\Active\\Job tracker",
"-f",
"E:\\Storage\\Projects\\Active\\Job tracker\\docker-compose.database.yml",
"--profile",
"integration",
"up",
"-d",
"--wait",
"--wait-timeout",
"120",
"mariadb-test"
],
"log": "E:\\Storage\\Projects\\Active\\Job tracker\\.artifacts\\mariadb\\compose-final\\compose-start.log",
"exitCode": 0
},
{
"command": [
"C:/Users/Connor/.dotnet/dotnet.exe",
"restore",
"JobTrackerApi.MariaDb.Tests/JobTrackerApi.MariaDb.Tests.csproj",
"--locked-mode"
],
"log": "E:\\Storage\\Projects\\Active\\Job tracker\\.artifacts\\mariadb\\compose-final\\restore.log",
"exitCode": 0
},
{
"command": [
"C:/Users/Connor/.dotnet/dotnet.exe",
"test",
"JobTrackerApi.MariaDb.Tests/JobTrackerApi.MariaDb.Tests.csproj",
"--configuration",
"Release",
"--no-restore",
"--logger",
"trx;LogFileName=mariadb.trx",
"--results-directory",
"E:\\Storage\\Projects\\Active\\Job tracker\\.artifacts\\mariadb\\compose-final"
],
"log": "E:\\Storage\\Projects\\Active\\Job tracker\\.artifacts\\mariadb\\compose-final\\tests.log",
"exitCode": 0
},
{
"command": [
"docker",
"compose",
"--project-name",
"jobtracker-it-bab4e2d90fda49aca25affafc282e335",
"--project-directory",
"E:\\Storage\\Projects\\Active\\Job tracker",
"-f",
"E:\\Storage\\Projects\\Active\\Job tracker\\docker-compose.database.yml",
"--profile",
"integration",
"logs",
"--no-color",
"mariadb-test"
],
"log": "E:\\Storage\\Projects\\Active\\Job tracker\\.artifacts\\mariadb\\compose-final\\server.log",
"exitCode": 0
},
{
"command": [
"docker",
"compose",
"--project-name",
"jobtracker-it-bab4e2d90fda49aca25affafc282e335",
"--project-directory",
"E:\\Storage\\Projects\\Active\\Job tracker",
"-f",
"E:\\Storage\\Projects\\Active\\Job tracker\\docker-compose.database.yml",
"--profile",
"integration",
"rm",
"--stop",
"--force",
"mariadb-test"
],
"log": "E:\\Storage\\Projects\\Active\\Job tracker\\.artifacts\\mariadb\\compose-final\\compose-cleanup.log",
"exitCode": 0
}
]
},
"runnerEvidence": {
"path": ".artifacts/mariadb/compose-final/run.json",
"sha256": "08ac2f67e8007e6b83ba2e812d745bf40a0f157d4054747b6ae681a41b9b609a",
"bytes": 3047
}
},
"mariaDbBrowser": {
"path": ".artifacts/mariadb/browser-mariadb.json",
"sha256": "debce9f0f52d1e54001c00972c7f93be80fa2b4a2c71f75360ce1e6a55dd662e",
"bytes": 8628,
"stats": {
"startTime": "2026-10-23T23:47:33.278Z",
"duration": 12135.389000000001,
"expected": 1,
"skipped": 0,
"unexpected": 0,
"flaky": 0
},
"productionQualification": false,
"process": {
"finishedUtc": "2026-10-23T23:47:45.5638011Z",
"startedUtc": "2026-10-23T23:47:32.6487673Z",
"exitCode": 0,
"generatedSchema": "jt_it_79b5902ffa2544a4a45a8eac4ae2ee3b",
"lane": "mariadb"
},
"provider": "actual pinned Docker MariaDB12.2.2; native hosted API/Chromium/independent SQL"
},
"historicalDiagnostics": {
"path": ".artifacts/mariadb/browser-historical.json",
"sha256": "ec9e78902125ea1edce586903cd2ea18ba0c85f0f6d85547f5d40ff96ca282fb",
"bytes": 1299337,
"stats": {
"startTime": "2026-10-23T19:06:02.726Z",
"duration": 517487.17799999996,
"expected": 18,
"skipped": 0,
"unexpected": 0,
"flaky": 0
},
"productionQualification": false,
"provider": "explicit transitional SQLite; never MariaDB qualification"
},
"historicalVolume": {
"path": ".artifacts/mariadb/browser-volume.json",
"sha256": "9511e090a792bc1c35043ebac6bee29d59d01511f37e10f08763560e9ead2bbb",
"bytes": 8513,
"stats": {
"startTime": "2026-10-23T19:14:43.927Z",
"duration": 71938.38900000001,
"expected": 3,
"skipped": 0,
"unexpected": 0,
"flaky": 0
},
"productionQualification": false,
"provider": "explicit transitional SQLite; never MariaDB qualification"
},
"guardTests": [
{
"path": ".artifacts/mariadb/runner-guards-final.log",
"sha256": "f0b946ced5ae5487e5849e289f84c3390686622aa746b4b55ce588b9f0d94868",
"bytes": 378,
"command": [
"python",
"scripts/test-mariadb-integration.py"
],
"exitCode": 0,
"passed": 5,
"realDatabaseEvidence": false
},
{
"path": ".artifacts/mariadb/browser-guards-final.log",
"sha256": "f3587abaf0d417b4b703416972bec2f3229e2e857c5d84fdf9ff275c7bac8c9c",
"bytes": 1731,
"command": [
"node",
"--test",
"job-tracker-ui/scripts/mariadb-browser-connection.test.mjs"
],
"exitCode": 0,
"passed": 10,
"realDatabaseEvidence": false
}
],
"static": {
"frontendTypecheck": {
"exitCode": 0,
"evidence": {
"path": ".artifacts/mariadb/typecheck.log",
"sha256": "f00c5da05f15ad139b5ce9c171cd10193f95e04a1348638144810e4e4f6cd497",
"bytes": 150
}
},
"frontendLint": {
"exitCode": 0,
"finalRerun": "after ESM browser guard conversion; exec session85067"
},
"targetedBrowserTypeScript": {
"exitCode": 0,
"command": "node node_modules/typescript/bin/tsc --noEmit --skipLibCheck --esModuleInterop --moduleResolution bundler --module esnext --target es2022 --allowJs playwright.mariadb.config.ts mariadb-tests/save-recovery.spec.ts"
}
},
"localDevelopment": {
"serverVersion": "12.2.2-MariaDB-ubu2404",
"migrations": 51,
"apiHealth": "{\"status\":\"ok\",\"version\":\"1.0.0.0\"}",
"sqlAfterDatabaseStopStart": "VERSION()\n12.2.2-MariaDB-ubu2404\nmigrations\n51\nId\tName\tOwnerUserId\n1\tRD21 persistent development probe\trd21-dev-persistence-probe\n",
"volume": "jobtracker-local_mariadb_dev",
"preexistingVolume": false,
"volumePreserved": true,
"productionTouched": false
}
},
"earlierFailures": [
{
"stage": "first actual MariaDB run",
"passed": 13,
"failed": 17,
"cause": "14 missing-Identity-column failures plus3 harness mistakes; not a passing qualification"
},
{
"stage": "schema census RED",
"missingColumns": [
"AspNetUsers.FirstName",
"AspNetUsers.LastName",
"AspNetUsers.DisplayName"
],
"fix": "guarded additive startup reconciliation; historical migrations untouched"
},
{
"stage": "held writer RED",
"dirtyTrackedEntries": 4,
"cause": "Pomelo1205 wrapped outside DbUpdateException catch",
"fix": "shared update cleanup catches wrapper and rethrows; no silent retry"
},
{
"stage": "Linux import history RED",
"failed": 1,
"cause": "DTO-projection ordering untranslatable",
"fix": "tenant-filtered entity ordering/limit before DTO projection"
},
{
"stage": "browser with stale Release binary",
"failed": 1,
"cause": "Debug-only fix build left old Release query",
"fix": "Release rebuild1123pass and final guarded Docker browser1pass"
},
{
"stage": "missing/unavailable/wrong-version guard probes",
"expectedFailure": true,
"qualificationPassed": false
}
],
"supplementaryOverlappingResults": {
"nativeWindowsMariaDb": {
"passed": 31,
"artifact": "mariadb.trx"
},
"officialLinuxFilesystemWsl": {
"passed": 32,
"artifact": "mariadb-linux-32.trx"
},
"earlierCompose": {
"passed": 32,
"artifact": "compose-integration/mariadb.trx"
}
},
"review": {
"independent": true,
"importantFindingsResolved": [
"canonical browser connection prevents duplicate/alias effective-target bypass",
"both browser servers refuse reuse",
"actual SELECT VERSION checked and attached",
"test service uses bridge to avoid per-project network leak"
],
"remainingActionableFindings": 0
},
"cleanup": {
"finalComposeContainerRemoved": true,
"finalComposeProjectHasNoGeneratedNetwork": true,
"earlierOwnedEmptyNetworkRemoved": true,
"browserTestContainerRemoved": true,
"developmentVolumePreserved": true
},
"verdicts": {
"mariaDb1222Rd21": "PASS_BOUNDED_RESOLUTION",
"localDevelopment": "READY",
"ciIntegration": "LOCAL_READY_HOSTED_UNVERIFIED",
"historicalFixtures": "PASS",
"sqliteRetirement": "PLAN_READY_REMOVAL_NO_GO",
"mariaDb123Lts": "INVESTIGATION_READY_MIGRATION_NO_GO",
"phase9Reacceptance": "NO_GO",
"phase10": "NO_GO",
"production": "NO_GO"
},
"preservation": {
"groups": 57,
"routes": 33,
"partial": 56,
"deferred": "C11",
"historicalAcceptanceReportsRewritten": false,
"userDataDeleted": false,
"historicalMigrationsChanged": false
},
"notQualified": [
"hosted Gitea execution",
"complete application MariaDB coverage",
"production image/config/environment equality",
"production deployment",
"12.3 upgrade/restore/rollback",
"SQLite user-data transfer",
"manual/device/provider/output wider Phase9 acceptance",
"power-loss/storage durability"
],
"sourceSha256AtImplementationCheckpoint": {
".env.example": "46259cf55ea73ddb5726e95f98480c5131e8a00519dba9cb1a6d196b1c2eac71",
".gitea/workflows/mariadb-integration.yml": "621f15d36edc6ea9260fb2586b24b53993ba6202fdcd731bf4babbbc3ab4b8d7",
"JobTrackerApi.MariaDb.Tests/JobTrackerApi.MariaDb.Tests.csproj": "2df22a5b79fae14566b437081932218254eb811c8dc1b2af3d51692d8e0d6825",
"JobTrackerApi.MariaDb.Tests/MariaDbDatabase.cs": "1375d97093f686f77d511ef6d81ad2d62ecf329aa6e7bf53036cd6e199035e40",
"JobTrackerApi.MariaDb.Tests/README.md": "75c687f453662d313a34b5d3c7bb3c957a18afb15a7014569b853d28d69b55f9",
"JobTrackerApi.MariaDb.Tests/Rd21CareerPersistenceTests.cs": "d8fe34ce82a7f111fb75673ff14d44df0b0de778a3e42b050cfbbd9f3c274331",
"JobTrackerApi.MariaDb.Tests/Rd21JobPersistenceTests.cs": "fa70202b58933f293ec6b9655a8a717506cd75ce5824703fa14e532a288267a8",
"JobTrackerApi.MariaDb.Tests/StartupAndLockTests.cs": "f36b0cbf43ac68738997249c6ec6f1963c27a3c350e3ebba9476f27ef7ed9f81",
"JobTrackerApi.MariaDb.Tests/TestHostFactory.cs": "f9f04e0aa7bad06863910564059605a13950540a5b6e04633f7ba7340719fdab",
"JobTrackerApi.MariaDb.Tests/packages.lock.json": "bfc1e47f3c80a061c1002a79db18fa46e1f996665d501a9126c479ae0713e74d",
"JobTrackerApi.Tests/DatabaseConfigurationTests.cs": "597e83474c1067b90647325a73eccd2fe479c4d5eabf3846b3af92ed92ac2d8b",
"JobTrackerApi/Controllers/JobApplicationsController.cs": "03cef66c86a2cb2df86764301e49a747df304abc82109d41467a5038fe01c335",
"JobTrackerApi/Controllers/ProfileCvController.cs": "8afae7680bc699ca525bb2c48cc8fba71922faed68abe21feb6046c3a8e70bda",
"JobTrackerApi/Data/DatabaseConfiguration.cs": "d07e323238b2d3f6a33dc688243a2d3143ffef8e797a412d48d1175587eaf7f5",
"JobTrackerApi/Program.cs": "1956baf3c208185e89cee46a4f9ae340b79d75fb6cb823cec982570dbcdbd201",
"JobTrackerApi/Services/DatabaseBackupRunner.cs": "aa9132378123b712c42c6f56c8fb001f5b86bb6fa2bd3dfce22ef73e06dba42f",
"JobTrackerApi/Services/StartupInitializationExtensions.cs": "4d60f14dc5069e27309f662c32dd15504dbe797dee9d46cb69460f818c0544c6",
"JobTrackerApi/appsettings.Development.json": "6a3efdb176f933bbdf9aa623d617e74e84e2b61cecd20fdef6ef77351554c3f2",
"docker-compose.database.yml": "bc4f0d19ef913f9e0f92540ed9a817e7bbf00d2f00ae80ed5b1acd43d55ba363",
"docker-compose.yml": "97aecfd456f5ad8de8597d1e301a63e1a5be5f3e181032d54c12175df1acfd1c",
"docs/redesign/phase-9/p9-05/diagnostics/data.spec.ts": "b33a99c3b280f7cac8ed4294e0a4a0a297f4761708e5414e3bf768f8146a7780",
"docs/redesign/phase-9/p9-05/diagnostics/sustained.spec.ts": "76db0ac115e4745b67186b116421e2d7c374bd2e8f87a2fd40f04215e7b463c2",
"docs/redesign/phase-9/p9-05/volume/volume.spec.ts": "9a3abd9f4a8fe558e643bf481dc9a0d1857975e9d532fda6c95e27cf437f7908",
"job-tracker-ui/mariadb-tests/save-recovery.spec.ts": "3dc4f32270325fe73db1855d62a7061629834ff6f03f22a8cbb0dad25395f4a8",
"job-tracker-ui/playwright.config.ts": "bbfb6e503d39397bf0b8445a00683197e228852af2579420807463add5c8a646",
"job-tracker-ui/playwright.mariadb.config.ts": "3b427d62ca39ac1e16597e9eb4fdbf6f9abd445a2ed9c3b49f2a8ca279b3e3fa",
"job-tracker-ui/scripts/mariadb-browser-connection.mjs": "2678f51649a007a25d356e6acd35cbec4eda6d89b713ea77309c4455ec0b41fc",
"job-tracker-ui/scripts/mariadb-browser-connection.test.mjs": "39218ff8f671e0f50f8df0bc486b6fbea7699e8959662ac3b4b6d2dfce13573d",
"scripts/mariadb-integration.py": "410cede3ca8794de99735a579ac626a34b6a56ee89c6ef63880d692153c7cd97",
"scripts/test-mariadb-integration.py": "39aa5618724e74b859aceaf814bf08b9ea63a54205452805caa810a302441a80"
}
}
+5 -1
View File
@@ -1,6 +1,10 @@
# Migrations and compatibility
The API applies EF Core migrations on startup and performs guarded legacy-schema reconciliation for SQLite and MariaDB/MySQL compatibility. Fresh and repeated disposable SQLite migration chains are the normal local verification; generated provider SQL is not proof of a live production migration.
The API applies EF Core migrations on startup and performs guarded legacy-schema reconciliation.
MariaDB is the standard; fresh/repeated/populated disposable MariaDB startup and persisted-state
tests are the database integration lane. Explicit SQLite compatibility remains temporarily for
existing data/legacy tests. Generated SQL and SQLite passes are not MariaDB runtime evidence.
See [current qualification](mariadb-standardisation/README.md); no production migration is authorized here.
SQLite startup explicitly enables write-ahead logging (WAL) before reconciliation opens the database file. This lets concurrent readers coexist with committed usage and preference writes; it changes journaling, not stored fields or ownership. MariaDB/MySQL initialization is unchanged. Use the existing `VACUUM INTO` backup runner for a consistent SQLite snapshot, including committed WAL contents; copying only a running database's `.db` file is insufficient. Disposable startup, held-reader/write and independent backup tests cover this boundary; they do not replace production restore qualification.
+2
View File
@@ -10,6 +10,8 @@ Phase 7 execution started: 2026-10-11, from synchronized `b531998` on `codex/ui-
## Current repository work
- [x] **MariaDB standardisation and RD21 qualification — 24 October 2026:** [acceptance](../database/mariadb-standardisation/ACCEPTANCE.md), branch `codex/rd21-mariadb-standardisation`, baseline `0d16a691`. Pinned official MariaDB 12.2.2 Docker lane 32/32 and native browser recovery 1/1; existing backend 1,123/1,123, repaired diagnostics 18/18 and volume 3/3. Local dev migrations/persistent volume verified. RD21 resolved within qualified scope; hosted CI unverified, SQLite removal/12.3 migration/Phase 9 reacceptance/Phase 10/production remain gated. Docker socket failure recovered with preserved socket-only archives; no persistent Docker data reset or production action.
- [x] **RD21 safe remediation/requalification checkpoints — 23 October 2026:** [dated addendum](../redesign/phase-9/rd21-remediation/ACCEPTANCE_ADDENDUM_2026-10-23.md), branch `fix/rd21-acknowledged-edit-loss`, based on final Phase 9 `8e7e3f6`; product checkpoint `1b89a77`. Original persisted loss and Career partial commit reproduced and locally repaired, both final review findings addressed. Final backend 1,109/frontend 934 (105 suites)/ordinary browser 362/contracts 23/model-disabled sidecar 56/new remediation 6/conforming volume 3 pass; types/lint/build and unchanged PDF oracles 24 fixtures/60 pages pass. Original diagnostics 16 pass/2 fail and volume 2 pass/1 fail retain obsolete unversioned Career-seed failures; no skips or concealed reruns. Formal RD21 **UNRESOLVED** pending required MariaDB runtime qualification; Phase 9/Phase 10/production **NO-GO**. Preserve 56 PARTIAL/C11 DEFERRED and 57 groups/33 routes; historical evidence unchanged. Reporting publication follows the addendum; no production operation or main merge.
- **P5-15-01R-03 — GO for bounded Analytics authentication return repair:** [record](../redesign/phase-5/p5-15/p5-15-01/analytics-auth-repair/README.md). Original AUTH-01 is RED→GREEN; safe complete pathname/query/hash/context, local retry/history/expiry/logout and native foreign404/tampered-target journeys pass. Fresh backend1,033/frontend490(+18)/browser134(+5cases, formerfailure nowpassing)/diagnostics2; saved-search3, Rules4, P2-D01, revision5 and fidelity16/36+settings8/24 remain green. All57 groups reconcile to56PARTIAL/1DEFERRED. Reproducer and code checkpoints are pushed; final evidence receives its own immediate push. Production NO-GO/main unchanged. Stop before P5-15-02/P5-13/Docker/B07/deployment; human/provider/device, production readiness, existing debt and the CV intermittent observation remain open.