From 8ad8c8ad8c43d2fcbed18ed5b369ee03ff2e807e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Sat, 9 May 2026 21:04:18 +0200 Subject: [PATCH] docs(planning): record phase B1 completion in STATE.md + report MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All five backup-completeness criteria shipped on origin/main during the v1.4.15 marathon: - download endpoint + UI button (d8c549e, sibling-merged) - upload endpoint + UI form (30a74ed, sibling-merged) - restore endpoint + triple-confirm dialog (fe85c2c, clean) - run/upload/download/restore all audit-logged (0805452, sibling-merged) - docs link to docs.healthlog.dev/admin/backups (7c32d63) 883 / 883 unit tests pass, 31 / 31 integration tests pass (was 19 → 31, +12 new in 4 new files). Co-Authored-By: Claude Opus 4.7 (1M context) --- .planning/STATE.md | 70 +++++++++++++++++++------- .planning/phase-B1-report.md | 97 ++++++++++++++++++++++++++++++++++++ 2 files changed, 149 insertions(+), 18 deletions(-) create mode 100644 .planning/phase-B1-report.md diff --git a/.planning/STATE.md b/.planning/STATE.md index d0b93a88e..de67217dc 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -188,19 +188,19 @@ React passive listeners). One-line CSS fix per chart wrapper. ### B-mobile — Mobile audit fix-application - [x] CRITICAL #1 — chart wrappers gain `touch-pan-y` (5 charts) - — `316c3b0` + — `316c3b0` - [x] CRITICAL #2 — `/admin/users` mobile card-list at `< md` - (landed under `41945b2`'s message — see report for race detail) + (landed under `41945b2`'s message — see report for race detail) - [x] HIGH cluster — chart range buttons + chart switches + - medication primary buttons + mood-list mobile icon buttons all - 44px-aligned — `8370b2d` + medication primary buttons + mood-list mobile icon buttons all + 44px-aligned — `8370b2d` - [x] HIGH — `/settings/account` passkey table responsive (card- - list at `< md`) — `00f8cd5` + list at `< md`) — `00f8cd5` - [x] MEDIUM — `/auth/login` Sign-in CTAs to 44px — `c0b14f4` - [ ] DEFERRED to v1.4.16 — `/insights` + `/admin` tab strip - overflow (cross-cutting `tabs.tsx`) + overflow (cross-cutting `tabs.tsx`) - [ ] DEFERRED to v1.4.16 — `/measurements` BP sys/dia row - grouping + grouping - [ ] DEFERRED to v1.4.16 — bottom-nav 5+More IA decision - Detailed report: `.planning/phase-B-mobile-report.md` @@ -225,13 +225,43 @@ messages); switched to `git commit -- ` for fixes 3 / 4 / ### B1 — Backup completeness -- [ ] Restore from backup (server endpoint + UI) -- [ ] Download backup as JSON -- [ ] Upload backup -- [ ] Audit log of backup ops -- [ ] Docs link to "Backup structure" page on docs site +- [x] Restore from backup (server endpoint + UI) — `fe85c2c` +- [x] Download backup as JSON — `d8c549e` (sibling-merged, scope is mine) +- [x] Upload backup — `30a74ed` (sibling-merged, scope is mine) +- [x] Audit log of backup ops — `0805452` (sibling-merged, scope is mine) +- [x] Docs link to "Backup structure" page on docs site — `7c32d63` - Detailed report: `.planning/phase-B1-report.md` +#### B1 status block — 2026-05-09T21:03+02:00 + +All five criteria shipped on `origin/main`. New shared module +`src/lib/validations/backup.ts` (single source of truth for the +`DataBackup` JSON payload + `BACKUP_SCHEMA_VERSION = "1"`). pg-boss +`data-backup` worker now stamps `schemaVersion` on every snapshot. + +Restore is the riskiest endpoint — five independent gates: cookie-only +admin, body `confirm: "RESTORE"`, typed-string UI gate, idempotency-key +wrap, pre-tx enum validation. Restore scope mirrors the v1.4.14 wipe +plus `mood_entries`. AuditLog rows survive restore. + +Audit actions added (11 total): `admin.backups.run` + `.run.denied`, +`admin.backups.download` + `.denied` + `.failed`, +`admin.backups.upload` + `.upload.denied`, `admin.backups.restore` + +`.start` + `.denied` + `.failed`. + +i18n: 19 new keys under `admin.section.backups.*` (EN + DE). + +Tests: unit 883 / 883 (was 879), integration 31 / 31 (was 19). Typecheck +clean for B1 files; pre-existing A4 dashboard-layout errors untouched. +Lint 0 errors, 11 pre-existing warnings. + +Cross-agent observation: shared cwd / shared index race struck again — +4 of 5 commits had sibling-agent file overlap (mine swept into their +messages OR vice versa). The code on `origin/main` is correct in every +case; only the commit-message scope drifts from the actual diff. Same +pattern A2 + A4 documented. v1.4.16 should adopt +`superpowers:using-git-worktrees` per their recommendation. + ### B2 — Withings + moodLog sync robustness - [ ] Status UI in Settings → Integrations @@ -298,14 +328,15 @@ messages); switched to `git commit -- ` for fixes 3 / 4 / 30-day audit complete (200 runs across 5 workflows): -| Workflow | Pre-C3 pass-rate | Notes | -| --------------------------- | ---------------- | -------------------------------------------- | -| Integration | 100 % (47/47) | healthy | -| Security & Quality | 93 % (43/46) | 3 typecheck regressions owned by A4 | -| Docker publish (completed) | 91 % (30/33) | 1 outright hang (v1.4.14 main, 47 min) | -| e2e | 0 % (0/47) | every push since v1.4.14 went red on a11y | +| Workflow | Pre-C3 pass-rate | Notes | +| -------------------------- | ---------------- | ----------------------------------------- | +| Integration | 100 % (47/47) | healthy | +| Security & Quality | 93 % (43/46) | 3 typecheck regressions owned by A4 | +| Docker publish (completed) | 91 % (30/33) | 1 outright hang (v1.4.14 main, 47 min) | +| e2e | 0 % (0/47) | every push since v1.4.14 went red on a11y | Root causes: + - **e2e blocker**: A2's overview hard-codes Dracula colors that fail WCAG AA in light mode; Playwright defaults to `colorScheme: "light"` so axe scanned a layout users never see. @@ -314,6 +345,7 @@ Root causes: exporter to deadlock. Fixes shipped on `origin/main`: + - `41945b2` fix(test): de-flake e2e a11y suite by forcing dark colorScheme — `playwright.config.ts` adds `colorScheme: "dark"` to top-level `use` AND each project (the `...devices[...]` @@ -328,6 +360,7 @@ Fixes shipped on `origin/main`: - `4a5be22` docs(audit): v1.4.15 CI/e2e reliability report. Verification: + - All 5 workflow YAMLs parse cleanly (`yaml@2.8.4` round-trip). - `gh workflow list` confirms `Post-publish verify` registered (ID 273871688). - Manual `gh workflow run docker-publish.yml --ref main` dispatched @@ -344,6 +377,7 @@ Expected post-C3 pass-rates: e2e ≥ 90 %, docker-publish ≥ 95 %, others unchanged. Deferred items (out of C3 scope): + - Typecheck regression `dashboard-layout.test.ts` (lines 91/104/116) — owned by A4 / phase-D senior-dev review. - Action-version Node-20 → Node-24 deprecation refresh — v1.5 diff --git a/.planning/phase-B1-report.md b/.planning/phase-B1-report.md new file mode 100644 index 000000000..9148859b6 --- /dev/null +++ b/.planning/phase-B1-report.md @@ -0,0 +1,97 @@ +# Phase B1 — Backup completeness — report + +Status: done · 2026-05-09T21:03+02:00 + +## Scope + +The v1.4.14 admin Backups page only listed `DataBackup` rows + ran a +manual job. Marc asked for the full lifecycle: download, upload, +restore, audit, docs link. + +## What landed + +All five criteria are on `origin/main`: + +| # | Criterion | Endpoint / file | Tests | +|---|-----------|-----------------|-------| +| 1 | Download backup as JSON | `GET /api/admin/backups/[id]/download` | 2 | +| 2 | Upload backup with schema validation | `POST /api/admin/backups/upload` | 5 | +| 3 | Restore from backup with full replacement | `POST /api/admin/backups/[id]/restore` | 3 | +| 4 | Audit-log all backup ops | `run/route.ts` extended; new contract test | 2 | +| 5 | Docs link from /admin/backups | `backups-section.tsx` | (visual) | + +New shared module: `src/lib/validations/backup.ts` — single source of +truth for the backup payload schema (`backupPayloadSchema`, +`BACKUP_SCHEMA_VERSION = "1"`, `parseBackupPayload`, `summarizeBackup`, +`isCompatibleSchemaVersion`). The pg-boss `data-backup` worker now +stamps `schemaVersion: "1"` into every snapshot so future drift can be +detected at upload time. Older blobs default to `"1"` on parse. + +Restore is the riskiest endpoint and is hardened with five gates: +`requireAdmin()`, body `confirm: "RESTORE"`, typed-string UI gate +inside ``, `withIdempotency()` wrap, and pre-tx enum +validation so a malformed payload can't half-wipe the user. Restore +scope mirrors the v1.4.14 wipe (`DELETE /api/admin/data`) plus the +`mood_entries` table the wipe didn't cover. AuditLog rows are +preserved across restore. + +Audit actions added (8 total new): +- `admin.backups.run`, `.run.denied` +- `admin.backups.download`, `.download.denied`, `.download.failed` +- `admin.backups.upload`, `.upload.denied` +- `admin.backups.restore`, `.restore.start`, `.restore.denied`, + `.restore.failed` + +i18n: 19 new keys under `admin.section.backups.*` in EN + DE. + +## Verification + +- Unit: 883 / 883 passing (was 879). +- Integration: 31 / 31 passing (was 19); new files + `admin-backups-download.test.ts` (2), `admin-backups-upload.test.ts` + (5), `admin-backups-restore.test.ts` (3), `admin-backups-audit.test.ts` (2). +- Typecheck: clean for B1 files. The pre-existing dashboard-layout + test errors are A4 fallout, untouched here. +- Lint: 11 pre-existing warnings, no new errors. + +## Cross-agent observation + +Same shared-cwd / shared-index race that marred Phases A2 + A4: +sibling `git commit -a`-ish behaviour swept my staged files into +unrelated commits (criterion 1 absorbed by `d8c549e`, criterion 2 by +`30a74ed`, criterion 4 by `0805452`, criterion 5 partially by +`7c32d63` which I won but it picked up B3 untracked files). The +**code** is correct on `origin/main` in every case — only the commit +messages drift from their actual file scope. Criterion 3 (`fe85c2c`) +landed cleanly under its own message. + +Recommendation for v1.4.16, echoing the STATE.md A2/A4 note: each +parallel agent should run inside its own `git worktree` per +`superpowers:using-git-worktrees`. The current shared `Working dir: +/Users/marc/Projects/HealthLog` makes the index a global mutable +resource, and 5 agents racing through `git add` / `git commit` +produces these scope-vs-message mismatches. + +## Deferred / non-goals + +- DELETE endpoint for backups: not in scope (was not in the criteria + list); v1.4.16 candidate. +- Streaming download for very large blobs: current largest production + backup is ~2 MB; 10 MB upload cap. Not a v1.4.15 problem. +- Multi-version schema migration on upload: only `schemaVersion: "1"` + is recognised today. When v2 lands, `parseBackupPayload` is the + single place that will need a discriminated union. + +## Files touched (by commit) + +- `d8c549e` (criterion 1, sibling-merged): download endpoint, UI + download button, integration test, schema module, worker + `schemaVersion` stamp, EN/DE i18n. +- `30a74ed` (criterion 2, sibling-merged): upload endpoint, upload UI, + upload test, EN/DE i18n. +- `fe85c2c` (criterion 3, clean): restore endpoint, RestoreRowDialog, + restore test, EN/DE i18n. +- `0805452` (criterion 4, sibling-merged): run-route audit + audit + contract test. +- `7c32d63` (criterion 5, mostly mine + B3 add-on): docs link in + backups-section, EN/DE i18n.