mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-07 04:12:04 +00:00
Mostly table column normalisation under docs/audit/ + planning, object- literal expansion in src/lib/ai/coach/snapshot.ts after the W4 F6 Apple Health metric block landed, and small wrap/unwrap line adjustments in the device + APNs routes / tests touched across W3/W4/W5 sessions. No behavioural change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
14 KiB
14 KiB
Phase D — SIMPLIFY review (v1.4.19)
Reviewer: simplify-auditor (one of six parallel D-phase reviewers)
Scope: git diff --name-only v1.4.18...HEAD (~80 files, src + tests + i18n)
Tone: per CLAUDE.md — flag premature abstractions, defensive code that
can't fire, speculative options, what-comments, leftover shims, and
4+-callsite repetition. Empty list valid.
Findings ordered by impact. "Apply autonomously" = whether the smell is mechanical enough to land without fresh design discussion.
F1 — AUTH_ACTION_LABELS map duplicated verbatim across two admin sections
- Files:
src/components/admin/login-overview-section.tsx:70-82src/components/admin/recent-audit-preview.tsx:50-62
- Type: Repetition (4+-callsite trigger met: 11 keys × 2 sites,
both touched in the same v1.4.19 patch when the new
auth.token.*rows landed) - Why it's a smell: Identical 11-key
Record<string, string>map built inline in two different components. v1.4.19 added three new keys (auth.token.autoissue.native,auth.token.refresh,auth.token.revoke); both files had to be edited in lock-step to pick them up. A fourth call site (e.g. an admin notification feed surfacing the same audit rows) would have to copy the same map a third time. The map is also constructed inside the component on every render even though it depends only ont— every locale flip rebuilds the same 11-entry literal in two places. - Suggested change: Extract once to
src/components/admin/_shared.tsx:Both call sites becomeexport function useAuthActionLabels(): Record<string, string> { const { t } = useTranslations(); return { "auth.register": t("admin.authRegister"), // … "auth.token.revoke": t("admin.authTokenRevoke"), }; }const AUTH_ACTION_LABELS = useAuthActionLabels(). - Risk: Very low — pure rename + re-import; no behaviour change.
- Apply autonomously?: yes
F2 — chipClass dead-assigned for two of three pill states
- File:
src/components/settings/integration-status-pill.tsx:79-98,121-125 - Type: Dead code (defensive code that can't fire)
- Why it's a smell: The
erroranddisconnectedbranches setchipClass = ""(lines 91, 96), butchipClassis only ever read insidestate === "connected" && chipClass(line 123). Two assignments and thelet chipClass: stringdeclaration exist solely to keep the switch exhaustive — they paint nothing. - Suggested change: Replace the local with a const inside the
connectedarm:Dropconst connectedChipClass = "border-dracula-green/30 bg-dracula-green/15 text-dracula-green"; // … className={cn( "max-w-full whitespace-nowrap", state === "connected" && connectedChipClass, className, )}chipClassfrom the switch entirely. (Keeping the switch forlabel+iconis fine — those are read in every state.) - Risk: Trivial — purely cosmetic refactor, no rendered output changes. The existing pill test suite covers all three branches.
- Apply autonomously?: yes
F3 — lang={locale} on every <Input type="datetime-local"> /
type="date" — 14 callsites, no shared wrapper
- Files (count: 14 callsites across 9 files):
src/app/onboarding/page.tsx:307src/components/settings/account-section.tsx:389src/components/settings/export-section.tsx:266, 279src/components/medications/intake-history-list.tsx:548, 562, 694, 708src/components/mood/mood-list.tsx:575src/components/mood/mood-form.tsx:138src/components/measurements/measurement-form.tsx:364src/components/measurements/measurement-list.tsx:590src/components/doctor-report/doctor-report-dialog.tsx:197, 214
- Type: Repetition (14 callsites, way past the 4+ threshold)
- Why it's a smell: v1.4.19 commit
ff6e184("hint browser locale on date and datetime inputs") sprinkled the samelang={locale}prop into every native date / datetime-local Input. The pattern is now the convention — but it's expressed by hoping every developer remembers to add the prop, rather than by a primitive that bakes it in. A future date input that ships withoutlang={locale}will silently regress (the visual format is purely browser-controlled, no test fails). - Suggested change: Add a thin
<DateTimeInput>/<DateInput>wrapper insrc/components/ui/date-input.tsx:Same forexport function DateInput(props: Omit<InputProps, "type" | "lang">) { const { locale } = useTranslations(); return <Input type="date" lang={locale} {...props} />; }DateTimeInputwithtype="datetime-local". Replace 14 call sites mechanically. The prop sprawl from individually pullinglocaleout ofuseTranslationsin 9 components disappears too. - Risk: Low — wrapper is a 6-line drop-in. The 14 replacements
are mechanical (
Input type="date"→DateInput, drop thelang={locale}and the destructure where the only reason forlocalewas the prop). One nuance: 5 of the call sites also uselocalefor other things (medication card formatting, etc.) — leave those destructures alone. - Apply autonomously?: yes (mechanical sweep with
grep-driven replacements; tests already cover the broader form behaviour)
F4 — Single-icon wrapper <div> left behind after card title removed
- Files:
src/components/admin/feedback-inbox-section.tsx:81-83src/components/admin/danger-zone-section.tsx:73-78src/components/settings/thresholds-editor-section.tsx:129-134
- Type: Dead code (post-removal scaffolding)
- Why it's a smell: Each of the three commits that dropped a
duplicate card title kept the
<div className="flex items-center gap-2">wrapper, which now contains exactly one icon. The wrapper was there to align icon+title on the same row; with the title gone the flex/gap classes do nothing. Reads as scar tissue from the removal patch. - Suggested change: Drop the wrapper, render the icon directly:
Same for
<Inbox className="text-primary h-5 w-5" aria-hidden="true" /><AlertTriangle>(danger-zone) and<SlidersHorizontal>(thresholds-editor). - Risk: Trivial — the spacing inside the card is governed by the
parent
mt-4/space-y-4/space-y-5already. - Apply autonomously?: yes
F5 — metaSlot prop on <InsightsPageHero> has exactly one caller
- Files:
src/components/insights/insights-page-hero.tsx:43-53,127-135src/app/insights/page.tsx:866(only caller passingmetaSlot)
- Type: Premature abstraction (single-callsite prop with speculative future-extensions framing in the JSDoc)
- Why it's a smell: The new
metaSlot?: React.ReactNodeprop exists solely to inject<CompareToggle />into the hero meta band on/insights. There is no second caller; the doc-string speculates about "page-level metadata slot". TheCompareTogglecould just as well be rendered aschildren(the hero already has structured content slots: generated/baseline/regenerate). Per CLAUDE.md "single-callsite helpers / single-implementation interfaces", this is the textbook case. - Suggested change: Either (a) inline the toggle inside the hero
for the single page that needs it, gated by a presence check on a
more explicit prop (
showCompareToggle?: boolean), or (b) accept it as an extra<InsightsPageHero>childrenslot with a data-slot rendering hook. Option (a) is simpler given there's exactly one caller; revisit if a second page needs the same band. - Risk: Low — the change is in one component + one caller. Tests
for
<InsightsPageHero>already use the prop sparingly (no fixtures passmetaSlot). - Apply autonomously?: no — design call (which prop shape is the right one) belongs to the next planning round, not a mechanical sweep.
F6 — Speculative permissive STRIP_TOKEN_REGEX for hypothetical
hallucinations
- File:
src/lib/insights/chart-tokens.ts:42-55 - Type: Speculative-options option (defending against patterns the AI might emit, with one verbatim-Marc anecdote as evidence)
- Why it's a smell: A3 split a single
metric:[A-Z_]+regex into a permissive strip-side (metric:[A-Za-z0-9_]+) and a strict parse-side. The justification ("Marc 2026-05-10:metric:blood_pressure_sweet_spot") is one anecdote; the comment speculates lowercase / digit / snake_case tokens "leak to the DOM" but the token allowlist filter happens before render, so the only real failure mode is the literal token string leaking into prose. Worth keeping if AI prose actually leaks lowercase tokens in non-trivial frequency, but the comment treats it as a permanent hardening when the test suite has only one regression test (stripChartTokenstests atchart-tokens.test.ts). The two-regex setup means future allowlist changes need to be cross-checked against both patterns. - Suggested change: Verify whether prose emissions of
metric:lowercase…are still happening in v1.4.19 advisor outputs (sample recent payloads from prod). If not, collapse back to the single uppercase regex and re-add the permissive strip only when a fresh regression appears. If still happening, leave the split but trim the verbose 12-line comment to the 2-line "why two regexes" gist. - Risk: Medium-low — needs an empirical check before reverting; the strict-parse path is correct either way (allowlist filters), so the worst case of premature collapse is a literal token leaking into UI prose for a few days.
- Apply autonomously?: no — wants a quick prod-payload sample before the call.
F7 — formatBackupType / formatMeasurementSource are
near-clone enum-translate helpers
- Files:
src/components/admin/backups-section.tsx:160-170src/components/measurements/measurement-list.tsx:107-122- (Also similar shape in
src/components/medications/intake-history-list.tsx:99-105—sourceLabelslookup in-component)
- Type: Repetition (3 callsites; below the 4+ threshold but worth noting because v1.4.19 added two of them in the same wave)
- Why it's a smell: Both helpers do the same job: take a
SCREAMING_SNAKE enum string + the i18n
tfunction, look upt(...key)for known values, fall back to the raw string. v1.4.19 Wave-B added these two in the same patch (both were "humanise raw enum badges" fixes). The pattern is genuine but the shape is per-enum because the i18n key prefix differs. - Suggested change: Leave alone for now — only 3 call sites and
the
tkeys differ per enum, so a generic helper would have to take the prefix as an argument and gain nothing in line count. Re-evaluate when a 4th appears (likely candidates:IntakeStatus,NotificationChannel). - Risk: n/a (the suggestion is "no action").
- Apply autonomously?: n/a
F8 — Verbose post-removal "v1.4.19 X removed because …" comments
- Files:
src/app/insights/page.tsx:226-232(getRangeColorClass/getRangeHintremoval note)src/app/insights/page.tsx:648-651(bf/showBodyFatCardremoval note)src/app/insights/page.tsx:856-861(compare-toggle move note)src/app/insights/page.tsx:868-880(onRegeneratedeletion note)src/app/page.tsx:494-503(compare-toggle removal-from-dashboard note)
- Type: WHAT-not-WHY scarring / narration of past commits
- Why it's a smell: Five different comments in two files
describe what used to be there in v1.4.18 and why it was removed
("
bf/showBodyFatCard… were bookkeeping for the now-removed duplicate tile strip", etc.). The git log + the v1.4.19 phase reports already record this. A fresh contributor reading the insights page does not need to know there used to be a tile strip there. Once these notes age past v1.4.20 they read as "history of the file" rather than "current invariants". - Suggested change: Strip all five paragraphs. Replace with a
single one-line
// see .planning/phase-A3-report.mdif a pointer is desired, or delete outright sincegit blameanswers "what changed in v1.4.19". Keep only the comments that describe current invariants (e.g. thepillStateForcollapse comment in integrations-section is fine — that one explains current behaviour). - Risk: Trivial — pure comment removal.
- Apply autonomously?: yes
Cleanup verifications (no findings)
- A5
IntegrationStatusPill— 2 callsites today (Withings, Mood Log) plus a documented v1.4.20 plan to add Apple Health. Above the single-callsite threshold; passes the abstraction-bar per CLAUDE.md. Clean. - A2
useViewportWidth+chooseTickInterval— used by 4 chart components (health, mood, compliance-line, medication-compliance) and the helper has its own unit-test suite. 4 callsites = right at the threshold, justified. Clean. - A1
computeBpInTargetWindowsallTimewindow — was the fix-the-headline change. The previous v1.4.18 review (F3) flagged the duplicatecomputeBpInTargetPct+computeBpInTargetWindowscall in the analytics route; v1.4.19 went the other direction and routed all three values through the windows helper, so the redundancy is now resolved. Clean — no further action. - A8 / Wave-B copy fixes — 27 commits, mostly
messages/{de,en}.json- small per-file edits. Spot-checked five files; no near-duplicate fixes that should share a helper. The two enum-translate helpers (F7) sit at 3 call sites, below threshold. Clean.
- A7 admin scrollbar / collapse fixes —
tabs.tsxgot theoverflow-y-hiddenclamp once + a single regression test. Theapi-token-overview-sectioncollapse-button removal didn't leave any deadexpandedstate orsetExpandedcalls (verified by reading the post-fix file end-to-end). Clean. pillStateFor— single-purpose API-state→pill-state collapsor insideintegrations-section.tsx. 2 call sites in the same file, pure function, ~7 lines. Could be inlined but is more readable as a named helper. Out of scope.
Tally
- 5 actionable autonomous-safe findings (F1, F2, F3, F4, F8).
- 1 design-discussion finding (F5 — single-callsite
metaSlot). - 1 wants empirical evidence first (F6 — split chart-token regex).
- 1 informational no-action (F7 — enum-translate near-clones).
- 0 architectural rewrites required.
done: 5 apply-yes, 2 apply-no