Pre-existing format drift accumulated across v1.4.x; running
prettier brings the whole tree back to baseline so future diffs
read clean. No behaviour change.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three new layers of coverage for the v1.4.18 expansion set:
- Unit (expansion-metrics.test.ts) — drives synthetic data through
every new evaluator (count by type, mood mean improvement, weekend
warrior, hidden Easter-egg triggers including the Berlin TZ
classifier for night-owl / early-bird) and the earnability flag
builder. 13 new assertions.
- Integration (achievements-expansion.test.ts) — round-trips real
prisma rows (MoodEntry's YYYY-MM-DD `date` string, Measurement
type discrimination, audit log row) through
`buildExpansionMetricValues` so a schema drift in any of the row
shapes surfaces here rather than at runtime. We don't drive the
full HTTP route in this spec because of pre-existing
medication_schedules.days_of_week migration drift.
- E2E (achievements.spec.ts) — the existing sidebar smoke gets a
sibling describe-block exercising all four card states (unlocked /
locked-earnable / hidden-locked / hidden-unlocked) and asserting
hidden-locked cards never leak their real strings to the rendered
HTML, which is the entire point of the "hidden" state.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three remaining e2e failures after the global-setup tour-completed fix
all came from cross-project sharing — specs that target desktop-only
DOM (the `aside[aria-label="Sidebar"]` logo + Achievements link)
were running in the chromium-mobile profile too, where the sidebar
is `hidden md:flex` by design. Added `test.skip(viewport.width < 768,
...)` guards so each spec only runs in the project that can satisfy
its contract.
The fourth failure was the `/api/dashboard/widgets` endpoint that the
A5 tile-strip work added since `onboarding-flicker.spec.ts` was
written. Without a stub the unauthenticated dashboard 500s on this
query and React Query's error boundary swallows the page render
before `<GettingStartedChecklist>` mounts. Added the stub to both
flicker specs (complete + incomplete-onboarding paths).
Pre-v1.4.15 the page silently dropped locked achievements, so the user
only saw what they had already earned. Now every badge renders — locked
ones are grayed out with a "Locked" badge, criterion hint
({current} / {target}), and a progress bar. Unlocked entries keep the
existing primary-gradient card with the "Completed on …" footer.
Achievements are grouped by a new `category` axis (medication, vitals,
security, engagement) — derived in `getAchievementCategory()` so a
metric is always in exactly one bucket. Inside a category, unlocked
items sort first; among locked items the closest-to-unlock one ranks
first so the user always sees their immediate next goal at the top.
The sidebar link to /achievements was already in place under the
user-area group; this commit only adds the new locked/grouped render
plus an e2e smoke spec that walks the desktop sidebar → click →
heading visible path.
i18n: 9 new keys under `achievements.*` (locked, criterionHint,
progressPercent, categories.{medication,vitals,security,engagement},
dashboardCard.{title,viewAll,empty}) — EN + DE. C4's parity audit will
sweep later.
Tests: +9 unit (groupByCategory ordering + page render in EN/DE) +
1 e2e (sidebar → /achievements). 909 / 909 unit pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>