docs(ops): record release-candidate audit findings
CI and Deploy / test (push) Failing after 1m8s
CI and Deploy / deploy (push) Has been skipped

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 <noreply@anthropic.com>
This commit is contained in:
cesnimda
2026-07-19 22:53:43 +02:00
parent 95646e1d53
commit 5c5a572cfc
2 changed files with 48 additions and 3 deletions
+4 -3
View File
@@ -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 |
+44
View File
@@ -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.