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) <noreply@anthropic.com>
This commit is contained in:
Marc-André BombeckandClaude Opus 4.7 committed 2026-05-10 03:21:21 +02:00
1 parent 5f7b9d8652
commit 54bbc68eff
4 files changed
+483 -84

No files matched your search

+36 -8
View File
@@ -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
+126 -74
View File
@@ -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 `<InsightAdvisorCard>` / 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 `<InsightAdvisorCard>` 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 — `<InsightsCardPreview>` (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 `<CompareToggle>` 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 `<input type="checkbox">` | **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 | `<InsightAdvisorCard>` 690-line god-component | **deferred** → v1.4.17 | n/a |
| H3 | senior-dev | `<RecommendationCard>` 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 — `<RecommendationFeedback>` re-uses canonical
`RecommendationFeedbackRequest` types.
- F6 — `formatRelativeTime` doc-comment trimmed (stale polyfill claim).
- F7 — `<RecommendationCard>` 26-line slot-architecture header → 4-line
WHY.
- F9 — `pickProviderType` mis-anchored PROMPT_VERSION paragraph
removed.
- F10 — `legacy-payload.ts` `UnknownRec` interface inlined.
- F12 — `<ConfidenceMeter>` 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".
+318
View File
@@ -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 `<InsightAdvisorCard>` /
`<RecommendationsGrid>` / `<RecommendationCard>` / `<ConfidenceMeter>`
/ `<RecommendationFeedback>`.** 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 — `<InsightsCardPreview>` (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 `<md` (mirror api-tokens / users patterns).
- **M2** — RecommendationFeedback success state has no path to
revoke / change verdict.
- **M3** — `<RecommendationsGrid>` border colours at `/70`
opacity not contrast-verified against `--card`.
- **M4** — `<ConfidenceMeter>` 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 `<InsightsSectionNav>` should add
`overscroll-x-contain` for iOS Safari back-swipe.
- **M8** — `<HostMetricsChart>` 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 `<input type="date">` 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** — `<ScatterCorrelationChart>` (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** — `<InsightAdvisorCard>` (690 lines) is a god-component.
Extract `_advisor/{hero-finding, inline-charts, data-quality-
drawer, legacy-payload-cta}.tsx`.
- **H3** — `<RecommendationCard>` (417 lines) approaching split
point. Extract `metricTypeToChartTypes` + `<CitationFootnote>`
- `<RationaleCard>`.
### MED (deferred)
- **M1** — provider-runner two near-identical fallback runners.
Inline-extract a generic `runChain<T>(chain, attempt)` higher-
order. (Same as code-review L3.)
- **M2** — `<RecommendationFeedback>` 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** — `<RationaleCard>` 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<T>`.
### LOW
- **L1** — duplicate `// ─── Section Separator ───` comment in
insight-advisor-card.tsx:284-286.
- **L2** — `<RecommendationFeedback>` 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/<queue>.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.
@@ -62,8 +62,9 @@ describe("<CompareToggle>", () => {
const html = render(<CompareToggle />);
// 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);