From 5c5a572cfc7807909b771a7c162b947947c79dee Mon Sep 17 00:00:00 2001 From: cesnimda Date: Sun, 19 Jul 2026 22:53:43 +0200 Subject: [PATCH] docs(ops): record release-candidate audit findings Adds the two issues found and fixed during the release-candidate audit to release-candidate-review.md: the follow-up reminder index that never created on MariaDB (fix in the preceding commit), and the nondeterministic timeline day-grouping test. Corrects database-ownership.md drift: the MariaDB startup scenarios now report 42 tables (measured in every scenario this audit), not the stale 40 from before the last Phase 5 tables were added, and adds the partially-migrated heal scenario (35 -> 42) that was verified. All claims reflect behaviour verified in this audit: 420 tests on Windows and Linux in both ICU modes, all three Docker images built, four database startup scenarios against live MariaDB 11 and SQLite, and a full backup -> restore -> app-start cycle. Co-Authored-By: Claude Opus 4.8 --- docs/infrastructure/database-ownership.md | 7 ++-- docs/release-candidate-review.md | 44 +++++++++++++++++++++++ 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/docs/infrastructure/database-ownership.md b/docs/infrastructure/database-ownership.md index 856dc0e..f71849b 100644 --- a/docs/infrastructure/database-ownership.md +++ b/docs/infrastructure/database-ownership.md @@ -150,9 +150,10 @@ All four scenarios, 2026-07-19, against MariaDB 11 and SQLite: | Scenario | Result | |---|---| -| Empty MariaDB | 40 tables created, app starts | -| Populated MariaDB, restart | idempotent — still 40 tables, rows preserved | -| Empty MariaDB via the Docker image | 40 tables created, app starts | +| Empty MariaDB | 42 tables created, app starts | +| Populated MariaDB, restart | idempotent — still 42 tables, rows preserved | +| Partially-migrated MariaDB (Phase 4/5 tables dropped) | healed 35 → 42, surviving rows preserved | +| Empty MariaDB via the Docker image | 42 tables created, app starts | | Fresh SQLite | 42 tables created, app starts | | Existing partially-migrated SQLite dev DB (34 tables) | upgraded to 44 tables, 13 applications and 8 companies preserved | diff --git a/docs/release-candidate-review.md b/docs/release-candidate-review.md index d6ba23e..d3e9534 100644 --- a/docs/release-candidate-review.md +++ b/docs/release-candidate-review.md @@ -21,6 +21,13 @@ re-verified by line number rather than by prose, and a full backup → restore was completed and recorded in [`docs/operations/production-backup-verification.md`](operations/production-backup-verification.md). +**Release-candidate audit, 2026-07-19.** A full verification pass — 420 backend tests (Windows + Linux, +both ICU modes), frontend tests/typecheck/build, all three Docker images, and all four database startup +scenarios run against live MariaDB 11 and SQLite — found and fixed two more issues: a follow-up reminder +index that never created on MariaDB (and logged a false rollback signal on every boot), and a +nondeterministic timeline test. Both are recorded below. Backup → restore → app-start was re-run +end to end. No open blocker remains in the code or deployment path. + **Verdict: one blocker remains, and it is external — CI (B2).** Nothing in the application or the deployment path is now known to be blocking. Everything verified here is *local* verification; CI has proven none of it. @@ -276,6 +283,43 @@ different claims — which is exactly what B2 below leaves unresolved. --- +### Follow-up reminder index never created on MariaDB — **CLOSED 2026-07-19** + +Found during the release-candidate audit, watching a real backend boot against an empty MariaDB. + +`IX_JobApplications_OwnerUserId_FollowUpAt` was declared `(OwnerUserId(191), FollowUpAt)` with **no +prefix length on `FollowUpAt`**. But `FollowUpAt` is `text` on MariaDB — `JobApplications` is +migration-owned and the migration was scaffolded against SQLite, which stores `DateTimeOffset` as +`TEXT`. A text column cannot be indexed without a prefix length, so the index failed the 3072-byte key +check on **every** MariaDB boot, was caught by `TryCreateIndex`, and was silently skipped. + +Two consequences, both real: + +- The follow-up reminder query (`OwnerUserId + FollowUpAt`) ran unindexed — the index the code says it + creates never existed. +- Every healthy boot logged `Specified key was too long` — the exact string + `deploy/first-production-deployment.md` lists as a **stop-and-roll-back** signal. An operator + following the runbook could abort a perfectly good deploy on a false alarm. + +**Fixed** by prefixing `FollowUpAt(20)`, matching the `Status(50)` fix the author already applied one +line below for the identical longtext problem. ISO-8601 date strings sort lexicographically, so a +20-char prefix stays useful for the reminder scan. + +*Verified* on a fresh empty MariaDB 11 container: index now created with both key parts, **zero** +"too long" lines, **zero** skipped indexes, **zero** unhandled exceptions, 42 tables, app healthy. +Audited the whole class — `FollowUpAt` was the only unprefixed text column in any reconciler composite +index; the datetime columns on reconciler-owned tables are `datetime(6)`. + +### Timeline day-grouping test was nondeterministic — **CLOSED 2026-07-19** + +`Timeline_groups_by_day_newest_first` seeded two "same day" events with +`DateTime.Now.AddDays(-3).AddHours(2)`, which crossed midnight whenever the wall clock was within two +hours of it — failing the test ~2 hours out of every 24, including in CI. The service is correct +(groups by `.Date`); the test was fixed to anchor the older events to `DateTime.Today` plus fixed +hours. Verified passing at 22:35 local (inside the failing window) and on Linux, both ICU modes. + +--- + ## BLOCKED — requires external action One item. Nothing in this repository can clear it.