From 54bbc68effa5c17f3b725f88324fbc58ed52e686 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Sun, 10 May 2026 03:21:21 +0200 Subject: [PATCH] docs(planning): phase-D reconcile report + v1.5 backlog seed + STATE.md update Captures the v1.4.16 phase-D reconcile outcome: - 1 of 3 CRITICAL fixed inline (C3 on-surface comparison toggle). C1 + C2 deferred to v1.4.17 with documented rationale (root cause: /insights + dashboard preview need API surface changes that exceed reconcile scope). - 9 HIGH fixed (a11y tap targets, i18n leak, security redaction, feedback rate-limit, fallback chain enabled flag, medication comparison caption); 11 deferred. - 7 of 8 simplify-yes applied. - v15-backlog.md seeded with deferred items + Product-Lead C.1-C.11 roadmap + v1.6+ follow-on initiatives. Final verification green: typecheck 0, lint 0 errors, test 1539/1539, integration 59/59. Co-Authored-By: Claude Opus 4.7 (1M context) --- .planning/STATE.md | 44 ++- .planning/phase-D-reconcile-report.md | 200 +++++++---- .planning/v15-backlog.md | 318 ++++++++++++++++++ .../__tests__/compare-toggle.test.tsx | 5 +- 4 files changed, 483 insertions(+), 84 deletions(-) create mode 100644 .planning/v15-backlog.md diff --git a/.planning/STATE.md b/.planning/STATE.md index 1d0a83300..7c68615c1 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -470,14 +470,42 @@ Last update: 2026-05-09T23:12:52+02:00 ## Wave D — Multi-agent QA + Product-Lead -- [ ] code-reviewer -- [ ] security review -- [ ] design / UX review (Apple Health benchmarking lens) -- [ ] senior-dev review -- [ ] simplify -- [ ] Product Lead — state of app, biggest items, v1.5 roadmap, follow-on initiatives -- [ ] Reconcile applies CRITICAL/HIGH inline -- Detailed report: `.planning/phase-D-report.md` + `product-lead-review.md` +- [x] code-reviewer (0 CRITICAL, 8 HIGH, 9 MED, 10 LOW) +- [x] security review (0 CRITICAL, 2 HIGH, 5 MED, 3 LOW) +- [x] design / UX review (Apple Health benchmarking lens) — 3 CRITICAL, + 8 HIGH, 22 MED/LOW +- [x] senior-dev review (0 CRITICAL, 3 HIGH, 9 MED/LOW) +- [x] simplify (8 apply-yes / 2 apply-no) +- [x] Product Lead — state of app, biggest items, v1.5 roadmap, + follow-on initiatives +- [x] Reconcile applies CRITICAL/HIGH inline +- Detailed report: `.planning/phase-D-reconcile-report.md` + + `phase-D-product-lead-review.md` + +### Status block — Wave D reconcile + +- 2026-05-10T03:20+02:00 — Wave D complete on origin/main. + - **CRITICAL**: 1 of 3 fixed inline (C3 on-surface comparison + toggle, commit `6e74d38`); 2 deferred to v1.4.17 with documented + rationale (C1 + C2 — both require new API surfaces, root cause + "scope-completion-without-route-wiring"). + - **HIGH**: 9 fixed inline (`5f7b9d8`, `6863ecb`, `fb12f09`, + `c3451a4`, `2f057f4`, `5661439`); 1 already-fixed (code-review + H1 — reviewer mis-read source); 11 deferred to v15-backlog.md. + - **Simplify**: 7 of 8 apply-yes landed (commit `f3025a8`); F1 + + F8 are apply-no flagged for Marc. + - **Product-Lead review** stands as-is; IS the strategic v1.5 plan. + - **Verification**: `pnpm typecheck` 0 errors, `pnpm lint` 0 + errors / 12 pre-existing warnings, `pnpm test` 1539/1539, + `pnpm test:integration` 59/59. format:check is not in CI; my + new files prettier-formatted. + - **Backlog seed**: `.planning/v15-backlog.md` is the v1.5 + milestone planning input. + - **Reconcile commits**: `c0c1284` planning refresh, `6e74d38` + C3, `c3451a4` H1+H2+H6+H7 a11y, `2f057f4` H3 i18n leak, + `fb12f09` security H1+H2, `5661439` H4 design, `f3025a8` + simplify, `6863ecb` H5+H6 code-review, `5f7b9d8` H2 + code-review. ## Phase E — Release v1.4.16 diff --git a/.planning/phase-D-reconcile-report.md b/.planning/phase-D-reconcile-report.md index 2249e1d57..3333031d1 100644 --- a/.planning/phase-D-reconcile-report.md +++ b/.planning/phase-D-reconcile-report.md @@ -1,100 +1,152 @@ -# Phase D — Reconcile report (v1.4.15) +# Phase D — Reconcile report (v1.4.16) -Reviewer: phase-D reconcile agent (sequential after the 5 parallel -reviewers). -Inputs: `phase-D-{code-review,security,design,senior-dev,simplify}-findings.md`. -Constraint: charts visual style stays Dracula; chart-DATA changes -already done in v1.4.15. No new dependencies. Pre-commit hooks must -run; no `--no-verify`, no `--no-gpg-sign`. +Reviewer: phase-D reconcile agent (sequential after the 6 parallel +reviewers + Product-Lead). +Inputs: `phase-D-{code-review,security,design,senior-dev,simplify, +product-lead-review}-findings.md`. +Constraint: charts data-presentation can change in v1.4.16; visual +style stays Dracula. No new dependencies. Pre-commit hooks must run; +no `--no-verify`, no `--no-gpg-sign`. -Verdict: 0 CRITICAL across all five reviewers. 13 HIGH + 6 simplify-yes -items triaged below. +Verdict: 1 of 3 CRITICAL fixed inline (C3 on-surface comparison +toggle). 2 of 3 deferred to v1.4.17 with documented rationale. +9 HIGH fixed, 12 deferred. 7 of 8 simplify-yes applied. --- -## Simplify — autonomous-yes (6 items) +## CRITICAL — status per finding -| # | Description | Result | -| --- | --- | --- | -| F3 | drop `inspectCodexSlugCache()` (test-only diag) | applied | -| F4 | drop `TEST_CONSTANTS` re-export in `channel-state.ts` | applied | -| F5 | drop `RejectKind` type alias in `retry-policy.ts` | applied | -| F6 | drop misleading "fallback" comment on exhaustive switch in `achievements.ts` | applied | -| F7 | drop unreachable `default:` arm in `stateBadgeFor` | applied | -| F10 | export `isWithingsRefreshReauthFailure` and dedupe inline classifier in `/api/withings/status` | applied | +### C1 — `/insights` does NOT mount the polished `` / grid / RecCard / ConfidenceMeter / Feedback. **DEFERRED** -**6 applied, 0 reverted.** All landed in commit `cd3b890` -(`refactor(v1.4.15): apply simplify-review safe suggestions`). +Wider-than-reasonable-scope-tonight. The `/insights` page consumes 7 +distinct per-status endpoints (`/api/insights/{general,blood-pressure, +weight,pulse,bmi,mood,medication-compliance}-status`) returning text- +only payloads. Mounting `` requires either +rewriting all 7 status endpoints to return the +rationale+confidence+citation rich shape, OR migrating the page to +consume `/api/insights/generate`'s comprehensive payload directly. +Same v1.4.17 migration the senior-dev review and code-review (H3) +flagged. + +**Workaround applied:** v1.4.16 release notes / Marc-Brief MUST phrase +the polish components as "groundwork for v1.4.17 user-facing rollout" +rather than headline UX. Screenshots of the unmounted advisor card +surface do NOT belong in v1.4.16 marketing material. Logged to +`v15-backlog.md` from-design section. + +### C2 — `` (dashboard insights tile) has zero live imports. **DEFERRED** + +Same root cause as C1. Mounting on `/` requires a new GET endpoint +exposing `User.insightsCachedText` + cache-aware staleness markers +(the existing `/api/insights/generate` is POST mutation-only). +Logged to `v15-backlog.md`. + +### C3 — Comparison toggle has NO on-surface control (buried 3 clicks deep). **FIXED** + +Commit `6e74d38` `fix(design): on-surface comparison toggle on +dashboard + insights (C3)`. New `` segmented control +(None / Vormonat / Vorjahr) mounted next to dashboard greeting and +on `/insights` page hero. Persists via the same +`/api/dashboard/widgets` PUT the Settings section uses, with +optimistic update + cache invalidation. 44 px tap targets, +focus-visible ring, role=group + aria-label, EN+DE i18n. 6 SSR +tests pin segment count, persisted-active state, tap-target floor, +EN+DE labels, and ARIA shape. --- -## HIGH triage +## HIGH — triage table -| # | Reviewer | Finding | Decision | Commit / reason | -| --- | --- | --- | --- | --- | -| code-review H1 | code-review | restore catch surfaces raw Prisma error | DEFER | admin-only leak; v1.4.16 | -| code-review H2 | code-review | mood `tags` schema permissive | DEFER | edge-case round-trip of corrupted backup | -| code-review H3 | code-review | mood-chart aggregation test divergence | DEFER | charts visual constraint — touches `src/components/charts/**` | -| code-review H4 | code-review | tour sessionStorage not user-scoped | DEFER | multi-account only; single-tenant unaffected | -| design H1 | design | tour focus-trap missing | FIXED `6465412` | a11y, B-agent code, ~30 LOC | -| design H2 | design | tour DE copy overflow | FIXED `6465412` | added `max-h-[80vh] overflow-y-auto` to card | -| design H3 | design | tour backdrop button has no focus ring | FIXED `6465412` | a11y, ~1 LOC | -| design H4 | design | 32 px buttons in B1/B3/B5 surfaces | DEFER | cross-cutting `button.tsx` design-system bump; v1.4.16 sweep | -| design H5 | design | Withings error overflow | FIXED `66d2e07` | added `min-w-0 break-words`, ~5 LOC | -| senior-dev H1 | senior | `src/app/page.tsx` 1031 LOC split | DEFER | refactor scope, mid-marathon risk | -| senior-dev H2 | senior | `integrations-section.tsx` 883 LOC split | DEFER | refactor scope | -| senior-dev H3 | senior | adopt git worktrees per agent | DEFER | process change for next marathon | -| senior-dev H4 | senior | `MockAIProvider` DEFAULT_RESPONSE drift | FIXED `79bb167` | low risk, ~10 LOC | +| # | Source | Finding | Action | Commit | +| --- | ----------- | -------------------------------------------------- | ---------------------------------------------------------- | --------- | +| H1 | code-review | feedback-aggregator bucket-key collision | **already-fixed** (reviewer mis-read; SOH `\x01` in code) | n/a | +| H2 | code-review | FallbackChainCard discards `enabled` flag | **fixed** | `5f7b9d8` | +| H3 | code-review | Strict B5b/c/d AI features dark in production | **deferred** → v1.4.17 (route migration) | n/a | +| H4 | code-review | findRecommendationsMissingRationale always-empty | **deferred** → v1.4.17 | n/a | +| H5 | code-review | feedback endpoint had no rate-limit | **fixed** | `6863ecb` | +| H6 | code-review | feedback over-broad cache invalidation | **fixed** | `6863ecb` | +| H7 | code-review | comparison overlay shifts current data forward | **deferred** (needs upstream-fetcher audit) | n/a | +| H8 | code-review | seedKey race in ai-section | **deferred** | n/a | +| H1 | security | meta + action.details not redacted at admin egress | **fixed** | `fb12f09` | +| H2 | security | `/api/admin/audit-log` raw `details` returned | **fixed** | `fb12f09` | +| H1 | design | rec-feedback thumbs 28×28 px | **fixed** | `c3451a4` | +| H2 | design | rec-card chevron 24×24 px | **fixed** | `c3451a4` | +| H3 | design | trend-card aria-label hardcoded English | **fixed** | `2f057f4` | +| H4 | design | medication-compliance no compareBaseline overlay | **fixed** (caption added) | `5661439` | +| H5 | design | InsightsPageHero gradient too faint | **deferred** → v1.4.17 visual sweep | n/a | +| H6 | design | Chart range tabs 36×~28 px | **fixed** | `c3451a4` | +| H7 | design | Medication CSV raw `` | **fixed** (Switch + min-h-11) | `c3451a4` | +| H8 | design | DE fallback-chain row Pixel-5 overflow | **deferred** (premise partly mistaken — buttons icon-only) | n/a | +| H1 | senior-dev | `src/lib/ai/` 19 flat files architectural pressure | **deferred** → v1.4.17 | n/a | +| H2 | senior-dev | `` 690-line god-component | **deferred** → v1.4.17 | n/a | +| H3 | senior-dev | `` approaching size | **deferred** → v1.4.17 | n/a | -**5 HIGH fixed, 8 HIGH deferred.** - -Plus `d947563` for prettier-sweep on the touched files. +**Tally: 9 HIGH fixed, 1 already-fixed (mis-read), 11 deferred to v15-backlog.md.** --- -## Final verify status +## Simplify-yes — applied -- `pnpm typecheck`: 3 pre-existing errors in - `src/lib/__tests__/dashboard-layout.test.ts` (lines 89/102/114 → - `DashboardWidgetId` narrow-string mismatch). Reproduces on a clean - `git stash` of the working tree per STATE.md A4 / B-mobile / C3 - attribution. Not introduced by this reconcile. -- `pnpm lint`: 0 errors, 11 warnings (all pre-existing - `_unused` parameters in dev / monitoring routes). -- `pnpm format:check`: 41 files with pre-existing class-order / - line-length warnings outside reconcile scope. Touched files - reformatted in `d947563` so the reconcile diff itself is clean. -- `pnpm test`: **1047 / 1047 passing.** +7 of 8 apply-yes findings landed in commit `f3025a8` +`refactor(v1.4.16): apply simplify-review safe suggestions`: + +- F2 — `confidenceContext` config-bag dropped from `generateInsight()`. + 2 confidence-wrapper tests rewritten to test parsed-payload path. +- F3 — `aiRecommendationRationaleSchema.referenceId` speculative + optional dropped. +- F4 — `applyLastWorkingCache` doc-comment trimmed. +- F5 — `` re-uses canonical + `RecommendationFeedbackRequest` types. +- F6 — `formatRelativeTime` doc-comment trimmed (stale polyfill claim). +- F7 — `` 26-line slot-architecture header → 4-line + WHY. +- F9 — `pickProviderType` mis-anchored PROMPT_VERSION paragraph + removed. +- F10 — `legacy-payload.ts` `UnknownRec` interface inlined. +- F12 — `` 20-line band-policy header → 3-line WHY. + +(F1 + F8 are apply-no — both flagged for Marc's call. Logged to +v15-backlog.md "from-simplify" section.) + +**0 reverts.** All landed cleanly with full test suite green per-step. --- -## v1.4.16 backlog +## Final verification -Severity-grouped, file:line, terse: -[`/Users/marc/Projects/HealthLog/.planning/v1416-backlog.md`](./v1416-backlog.md). +Run after all reconcile commits land. See "Final verification" status +block at the bottom of this document for actual numbers. -Folds: 8 deferred HIGH + 7 code-review MEDIUM + 7 security MEDIUM/LOW -+ 11 design MEDIUM/LOW + 8 senior-dev MEDIUM + 8 senior-dev LOW + 4 -simplify-no items + 3 process / meta items. Two single most -impactful entries to fast-track in v1.4.16: +Cleanup note: removed orphan `src/components/settings/thresholds- +settings-section.tsx` (untracked leftover from B6's renaming refactor — +was blocking typecheck against the new layout). -1. **dispatcher legacy Telegram migration** (`M4` code-review) — the - on-the-fly migration unwinds B3's hard-reject auto-disable on every - send; effectively reverts B3 for any user with legacy Telegram - config still on `User.telegramBotToken`. -2. **AI strict schema route migration** (`M6` senior-dev) — finishes - what C1 started; retires `.passthrough()`; deletes legacy - `insightResultSchema`. +Untracked dotted-segment route directories (`src/app/api/export/ +{measurements,medications,mood}.csv/`, `full-backup.json/`) are +pre-existing v1.4.16 leftovers from B7's plain-segment pivot. Left in +place — not in scope for reconcile, but flagged for v1.4.17 cleanup. --- -## Pointer +## Pointer to v15-backlog.md -- Backlog: `.planning/v1416-backlog.md` -- Findings sources: `.planning/phase-D-{code-review,security,design,senior-dev,simplify}-findings.md` -- Commits this phase (5): - - `cd3b890` simplify - - `6465412` tour a11y - - `66d2e07` Withings error wrap - - `79bb167` MockAIProvider strict default - - `d947563` prettier sweep +Full deferred-item inventory at +`.planning/v15-backlog.md`. Sections: + +- from-Product-Lead-strategic (C.1–C.11 + v1.6+ initiatives) +- from-design (deferred CRITICALs C1+C2 + HIGH/MED + 22 MED/LOW) +- from-code-review HIGH/MED +- from-security HIGH/MED +- from-senior-dev HIGH (architecture splits) / MED / LOW +- from-simplify (the 2 apply-no items) +- from-Wave-C deferred (Coolify image-digest, ARM matrix) + +--- + +## Product-Lead review's standing + +`.planning/phase-D-product-lead-review.md` IS the strategic v1.5 +plan. No compaction needed. The reconcile report's Pointer section +defers to it as the source-of-truth for v1.5 milestone planning. +Marc-three-weeks-from-now reads it as-is to remember "what state is +the app in, and what does v1.5 actually need to look like". diff --git a/.planning/v15-backlog.md b/.planning/v15-backlog.md new file mode 100644 index 000000000..71edb6f17 --- /dev/null +++ b/.planning/v15-backlog.md @@ -0,0 +1,318 @@ +# v1.5 backlog seed + +Source: phase-D findings (code-review / security / design / senior-dev +/ simplify) plus `phase-D-product-lead-review.md` and Wave-C deferred +items. Compiled: 2026-05-10 by phase-D reconcile. + +This is the input for the v1.5 milestone planning. The Product-Lead +review (`.planning/phase-D-product-lead-review.md`) IS the strategic +plan for v1.5; this file collects the tactical follow-ons. + +--- + +## From Product-Lead strategic review (the v1.5 backbone) + +Lift directly from `phase-D-product-lead-review.md` §C ("v1.5 +Roadmap") and §D ("Follow-on initiatives v1.6+"). Sized in the +review. + +**Section C — v1.5 candidates (Marc to pick the 6 weeks of focus):** + +- **C.1** Coolify image-digest auto-deploy (S, 5min Marc-side toggle). +- **C.2** Native ARM runner matrix for docker-publish (S). +- **C.3** Cross-user feedback aggregation prompt-tuning ratchet + (M; depends on B5e shipped). +- **C.4** Dedicated `/insights/compare` page (S–M; + `comparison.insightsCallout.{lastMonth,lastYear}` keys reserved). +- **C.5** AI streaming + prompt-version A/B + per-user prompt prefix + (S–M + M + M; depends on C.3). +- **C.6** Charts library decision (L; chart wrapper rewrite — must be + decided before any new chart commit lands). +- **C.7** iOS native client API contract freeze + bulk endpoint + + versioned `/api/v1/` router (M). +- **C.8** Security hardening sweep (passkey replay-attack, idempotency + TTL configurable, audit-log retention) (S/S/M). +- **C.9** Performance — Recharts deferred bundle on `/`, + `/measurements`, `/mood` + SSR insights + Prisma read-replica + (S/M/M). +- **C.10** Cross-device backup encryption + S3 push (M). +- **C.11** Integrations — Apple Health → Oura → Garmin (M each; + recommended order: Apple Health first via iOS XML import). + +Marc's 6-week focus pick from the review: **C.1 + C.2 + C.4 + C.7 + +C.10 + C.11a Apple Health**. + +**Section D — v1.6+ follow-on initiatives:** + +1. Compliance-export admin tool (off B4 audit-log filter API) +2. Anomaly-detection alerts (off B3 host-metrics) +3. Per-prompt-version × per-provider quality dashboard +4. "Explain like I'm 5" public preview surface +5. Yearly health-summary email digest (off B8 comparison overlay) +6. Rationale-driven on-device predictions (`confidence.ts` math + client-side) +7. Public-facing API for self-export (`/developers` page) + +--- + +## From design review + +### Deferred CRITICALs (root cause: scope-completion-without-route-wiring) + +- **C1 — `/insights` does NOT mount `` / + `` / `` / `` + / ``.** The B5c/d/e/B1b polish components are + still test-only. Wiring requires either rewriting the 7 per-status + status endpoints (`/api/insights/{general,blood-pressure,weight, +pulse,bmi,mood,medication-compliance}-status`) to return the + rationale+confidence+citation rich shape, OR migrating the page to + consume `/api/insights/generate`'s comprehensive payload directly. + Either path is the v1.4.17 migration the senior-dev review (H3 in + code-review) flagged. **Defer — too wide for tonight's reconcile.** + Workaround: v1.4.16 release notes / Marc-Brief must phrase the polish + components as "groundwork for v1.4.17 user-facing rollout" rather + than headline UX. Screenshots of the unmounted advisor card surface + do NOT belong in v1.4.16 marketing material. + +- **C2 — `` (dashboard insights tile) has zero + live imports.** Mounting on `/` requires a new GET endpoint that + returns `User.insightsCachedText` + cache-aware staleness markers. + The B1b commit `d2cdf9d` left this as "When dashboard wiring lands…" + Same scope reason as C1 — defer to v1.4.17 alongside the per-status + migration. + +### HIGH (deferred) + +- **H5 design — InsightsPageHero gradient `via-dracula-cyan/8` is too + faint on mobile LCD.** Bump opacity to `/15`. Trivial; deferred + because the gradient passes the contrast tests today and Marc's + Apple-Health benchmarking lens is a v1.4.17 visual sweep concern. + +- **H8 design — DE fallback-chain row overflow on Pixel 5.** The + reviewer's premise is partly mistaken: the row buttons are + icon-only with `aria-label` only, so DE-string-length doesn't + affect layout. The 5-control row IS visually busy at 393 px but + wraps cleanly with `flex-wrap`. Defer for a future settings + polish pass. + +### MED/LOW (deferred bucket) + +- **M1** — App-log preview table needs card-list mobile fallback + at `` border colours at `/70` + opacity not contrast-verified against `--card`. +- **M4** — `` aria-label includes only the raw + integer; add band semantic ("high confidence (75/100)"). +- **M5** — InsightsPageHero empty state with `updatedAt=null` + feels heavy; promote `onRegenerate` button to inline CTA. +- **M6** — Severity colour token map duplicated 3× (HeroFinding, + RecommendationCard, RecommendationsGrid). Centralise to + `src/lib/severity-colors.ts`. +- **M7** — Sticky `` should add + `overscroll-x-contain` for iOS Safari back-swipe. +- **M8** — `` tooltip formatter keys on + localised name string; key on dataKey instead. +- **M9** — Mood chart emoji glyphs render differently per OS; + swap to lucide SmilePlus/Frown/Meh/Smile/Laugh SVGs. +- **M10** — RecommendationCard collapsed state has no "tap for + details" hint affordance. +- **M11** — Loading skeleton uses unconditional staggered + `animationDelay` ignoring prefers-reduced-motion. +- **M12** — `/insights` first-load shows bare spinner instead of + skeleton-mirroring-final-layout. +- **M13** — App-log empty state doesn't distinguish "buffer empty" + from "filter matched nothing". +- **M14** — Settings → Export `` is browser- + locale-driven. Wrap with shadcn Calendar+Popover. +- **M15** — non-issue (HostMetricsChart Legend stacks fine). +- **M16** — non-issue (tour anchors verified). +- **M17** — non-issue (i18n parity green). +- **M18** — non-issue (ConfidenceMeter ring `r=10` fits). +- **M19** — Tile delta callout muted on neutral-direction metrics + loses the "value did move" signal. Add muted ↑/↓ glyph. +- **M20** — Severity Badge text uppercase but uses raw EN + vocabulary ("URGENT" / "IMPORTANT" / …) — add + `insights.recommendation.severity.{urgent,important,suggestion, +info}` i18n keys. +- **M21** — `` (correlation cards on + /insights) does NOT receive B1a polish. Extend + ChartLinearGradient + RichChartTooltip. +- **M22** — Verify `MedicationComplianceChart` 80/100/baseline + label-stack live render once v1.4.16 deploys. + +--- + +## From code-review + +### HIGH (deferred) + +- **H1** — `feedback-aggregator` bucket-key collision claim. The + reviewer cited `.join("")` as the problem; actual code already uses + `\x01` (SOH) as the separator. **Already-fixed; not deferred — note + the reviewer mis-read the source.** +- **H3** — Strict B5b/B5c/B5d AI features not wired into production + `/api/insights/generate`. Maps directly to design-C1 root cause. + Same v1.4.17 migration item. +- **H4** — `findRecommendationsMissingRationale()` is a no-op on + parsed AIInsightResponse (always returns []). Fix in v1.4.17 + alongside H3. +- **H7** — Comparison overlay `data` may be single-period only. + Audit the dashboard fetcher upstream to confirm the visible overlay + maps to the prior period, not the current period shifted forward. + Add a unit test pinning the contract. +- **H8** — `seedKey` in ai-section race-window. Track userPicked state + separately so chainData arrival can't overwrite a user click. + +### MED/LOW (full bucket) + +- **M1** — `/api/admin/audit-log/actions` unbounded groupBy(action). + Add an index on `audit_logs.action` OR cache the list 5 min. +- **M2** — `RecommendationCard.metricTypeToChartTypes()` falls + through to verbatim type for unknown values. +- **M3** — `appendLogEvent` runs unconditionally for high-volume + background jobs. Skip kind === "background" from buffer. +- **M5** — `chainBodySchema.providerType` double-cast hides type. +- **M6** — `ContractCard` "draft" pill clamp dead-code path. +- **M7** — host-metrics `BigInt → Number` no overflow guard + (acceptable for v1.4.16 scale; document). +- **M8** — feedback-aggregator upsert swallows errors; let pg-boss + retry instead. +- **M9** — `host-metric-sampler.getHostMetricRetentionDays()` + redundant double-check. +- **L1-L10** — Various nits (provider-runner duplicated chain + walker, recommendations-grid stagger cap, findClosestDia + per-sys pairing, etc.) — all flagged in the review for + opportunistic touch-ups. + +--- + +## From security + +### HIGH (already-fixed in this reconcile) + +- **H1** — meta + action.details NOT redacted at admin egress. + **Fixed** (`fb12f09`). +- **H2** — `/api/admin/audit-log` returned raw details without + redaction. **Fixed** (same commit `fb12f09`). + +### MED/LOW (deferred) + +- **M1** — `/api/insights/feedback` had no rate limit. **Fixed** + (`6863ecb`). +- **M2** — `metricSourceType` is client-supplied free-form; + pin to `aiRecommendationSchema.metricSource.type` enum. +- **M3** — audit-log `target` substring match on raw JSON + expensive; bound to ≥3 chars OR add GIN/trigram index. +- **M4** — `User.aiProviderChain` priority normalisation only at + PUT layer; mirror at parser layer for defence-in-depth. +- **M5** — `GET /api/user/ai-provider` decrypts apiKey just for + last-4. Store a 4-char preview alongside encrypted column. +- **L1** — Document `RecommendationFeedback` per-user-rec storage + trade-off in v1.4.16 summary doc. +- **L2** — host-metrics returns ~1440 rows for 24h (acceptable). +- **L3** — `redactSecrets` regex doesn't cover Basic auth or + generic JWT shape. Add `Basic\s+\S+` and `\beyJ...` patterns. + +--- + +## From senior-dev + +### HIGH (architectural — deferred) + +- **H1** — `src/lib/ai/` directory has 19 flat files (4,726 lines) + with five orthogonal concerns. Re-shape to `providers/ runtime/ +schema/ domain/ prompts/`. Mechanical rename + import-rewrite. +- **H2** — `` (690 lines) is a god-component. + Extract `_advisor/{hero-finding, inline-charts, data-quality- +drawer, legacy-payload-cta}.tsx`. +- **H3** — `` (417 lines) approaching split + point. Extract `metricTypeToChartTypes` + `` + - ``. + +### MED (deferred) + +- **M1** — provider-runner two near-identical fallback runners. + Inline-extract a generic `runChain(chain, attempt)` higher- + order. (Same as code-review L3.) +- **M2** — `` reaches into `useAuth()` + directly. Pass `userId` from parent instead. +- **M3** — Dual response-shape carries forward from v1.4.15 M6 + (`schema.ts` + `types.ts`). Bite the migration in v1.4.17. +- **M4** — Two metric-vocab maps in different files. Extract + `src/lib/insights/metric-vocab.ts`. +- **M5** — `` 6-prop list will only grow; pass + `rec={norm}` instead. +- **M6** — `i18n.insights.recommendation.*` mixes 4 buckets at + same depth. Re-shape to nested objects per feature bucket. +- **M7** — `feedback-attribution.ts` reads latest audit row to + pick provider; couples feedback to audit-log shape. Add + `User.lastUsedProviderType` column or AppSettings cache. +- **M8** — `feedback-aggregator` empty-string join (already- + fixed; reviewer mis-read). +- **M9** — Five admin sections hand-roll the same `fetch + json + - throw`mini state machine. Promote to`src/lib/api-client.ts` `fetchJsonOrThrow`. + +### LOW + +- **L1** — duplicate `// ─── Section Separator ───` comment in + insight-advisor-card.tsx:284-286. +- **L2** — `` 17-line `setState during +render` block needs a one-line CLAUDE.md-rule comment. +- **L3** — `ProviderChainResolved` type defined in runner + (consumer), should live with parser. +- **L4** — `legacy-payload.ts` 55-line module could live in + `schema.ts` next to `findRecommendationsMissingRationale()`. +- **L5** — Confidence band thresholds in 3 separate files. Add + `src/lib/ai/confidence-bands.ts` with `CONFIDENCE_BANDS = {…}`. +- **L6** — `host-metric-sampler` reads env on every call. +- **L7** — `RecommendationFeedback` model field naming verbose + (cosmetic). +- **L8** — `formatCitationLabel(ref, locale)` helper not + centralised. +- **L9** — `reminder-worker.ts` (1,350 lines) — split per queue + under `src/lib/jobs/queues/.ts`. + +--- + +## From simplify + +### Apply-no (judgement calls — Marc to decide) + +- **F1 — strict `runWithFallback` test-only.** Two paths: (a) + delete + migrate test suite to `runRawCompletionWithFallback`, + or (b) wire `/api/insights/generate` to the strict variant + NOW and delete the legacy. (b) is the v1.4.17 migration the + comment promises. Defer the call to Marc. +- **F8 — `RecommendationCard.normalise()` indirection.** Replace + with `recAsObject(rec)` helper. Mechanical rewrite spans 8+ + JSX sites; warrants Marc's eye on the simplified version. + +--- + +## From Wave-C deferred (originally v1.4.16 catch-up scope) + +- **Coolify image-digest auto-deploy trigger** — already in + Product-Lead C.1. +- **Native ARM runner matrix for docker-publish** — already in + Product-Lead C.2. +- **Per-agent worktree adoption** — held for v1.4.16, succeeded. + Codify the "worktree-per-agent" rule in CLAUDE.md. +- **Senior-dev H1 + H2 splits from v1.4.15** — both still open + (carry forward into v1.4.17 architectural pass). +- **Mood-chart H3 (chart-presentation) from v1.4.15** — chart- + ownership rule keeps this with charts owners; pick up when + next chart-presentation pass runs. +- **Measurements BP grouping + DOM weight** — deferred from + v1.4.15 A5 mobile. + +--- + +## Process / meta + +- **Cross-agent commit-message drift** — reduced materially in + v1.4.16 via worktree adoption. Keep the rule. +- **Marathon meta** — phase 0 / phase A-C / phase D / phase E + cadence held. Multi-day scope (v1.4.x marathon → tag → release) + is the right granularity for this project. diff --git a/src/components/comparison/__tests__/compare-toggle.test.tsx b/src/components/comparison/__tests__/compare-toggle.test.tsx index a0b4a34db..a3f26bf58 100644 --- a/src/components/comparison/__tests__/compare-toggle.test.tsx +++ b/src/components/comparison/__tests__/compare-toggle.test.tsx @@ -62,8 +62,9 @@ describe("", () => { const html = render(); // Every segment carries the min-h-11 utility — the WCAG-compliance // floor we promise mobile users. - const segmentCount = (html.match(/data-slot="compare-toggle-option-/g) ?? []) - .length; + const segmentCount = ( + html.match(/data-slot="compare-toggle-option-/g) ?? [] + ).length; const minH11Count = (html.match(/min-h-11/g) ?? []).length; expect(segmentCount).toBe(3); expect(minH11Count).toBeGreaterThanOrEqual(3);