From 4ab0d178081f56befe2e461c544d76144d56a676 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Thu, 14 May 2026 21:48:11 +0200 Subject: [PATCH] docs(planning): W15 hygiene cluster phase report MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wave 5 hygiene wave summary covering the four atomic commits and their verification matrix: fddb9754 — 380 dead i18n keys retired (25 false-positives kept) 41c7425b — BASE_SYSTEM_PROMPT + INSIGHTS_SYSTEM_PROMPT removed 61d156e7 — safeRequestProp narrow-catch + Tailwind @source base 76dea94b — Cat-C comment path rewrite on /api/insights/targets --- .planning/phase-W15-v1425-hygiene-report.md | 296 ++++++++++++++++++++ 1 file changed, 296 insertions(+) create mode 100644 .planning/phase-W15-v1425-hygiene-report.md diff --git a/.planning/phase-W15-v1425-hygiene-report.md b/.planning/phase-W15-v1425-hygiene-report.md new file mode 100644 index 000000000..5acef4bdc --- /dev/null +++ b/.planning/phase-W15-v1425-hygiene-report.md @@ -0,0 +1,296 @@ +# Phase W15 — v1.4.25 Hygiene Cluster + +Branch: `develop` · Started: 2026-05-14 21:30 · Finished: 2026-05-14 21:50 + +## Scope + +Four hygiene items rolled up under one wave so the locale-bundle and +api-handler surfaces do not get touched twice across parallel agents: + +1. Dead i18n key cleanup from the W10 runtime probe (414 candidate + entries — actual removable count subject to per-key spot-check). +2. Deletion of the two `@deprecated` system-prompt constants + `BASE_SYSTEM_PROMPT` + `INSIGHTS_SYSTEM_PROMPT`. +3. W7d hardening: narrow `safeRequestProp` catch + Tailwind `@source` + path resolution anchored to the project root. +4. Cat-C typo polish on the `/api/insights/targets` route comment that + referenced a wrong-pathed prompt directory. + +Touch boundary: this wave owned `messages/*.json` exclusively; W18 + +W20-rest stayed out of the locale files. The api-handler narrow-catch ++ `@source` change are dev-infra hardening, not user-visible. + +## Commits + +``` +fddb9754 chore(i18n): drop 380 dead translation keys flagged in W10 runtime probe +41c7425b chore(ai): drop dead BASE_SYSTEM_PROMPT and INSIGHTS_SYSTEM_PROMPT constants +61d156e7 fix(api-handler): narrow safeRequestProp catch + harden @source path resolution +76dea94b chore: Cat-C typo and naming polish from W10 dead-code probe +``` + +## Commit 1 — Dead i18n key cleanup (`fddb9754`) + +Started from the 405-entry list at `/tmp/w10-i18n-unused-list.txt` +generated by the W10 reconcile probe. A second-pass verification +script (`/tmp/w15-final-dead-keys.mjs`) re-scanned every `*.ts` / +`*.tsx` file under `src/` for exact-string matches in three quote +flavours (`"key"` / `'key'` / `` `key` ``). The verification surfaced +**25 false-positive dead-keys** — keys the static analyser missed +because they appear inside template literals or wrapped expressions +that the probe regex did not parse: + +| key | live caller | +| --- | --- | +| `charts.heatmapLate` | `src/components/charts/compliance-heatmap.tsx:257` | +| `charts.heatmapOnTime` | same | +| `charts.heatmapVeryLate` | same | +| `charts.movingAverage7d` | `src/components/charts/health-chart.tsx:1566` | +| `charts.trend` | `src/components/charts/health-chart.tsx:1581` | +| `doctorReport.{avgBp,classification,createdOn,dateOfBirth,gender,height,patient,period,practiceLabel}` | `src/lib/doctor-report-pdf-core.ts` + sibling | +| `medications.{importDuplicatesSkipped,importInvalidSkipped}` | `src/app/medications/page.tsx` | +| `medications.{nextScheduleDaily,nextSchedulePrefix,scheduleEveryNWeeks,scheduleIntervalPrefix}` | `src/components/medications/medication-form.tsx` | +| `medications.{pausedSince,yesterday}` | `src/components/medications/{glp1-,}medication-card.tsx` | +| `telegram.{helpBody,helpHeader}` | `src/app/api/telegram/webhook/route.ts` | +| `thresholds.defaultLabel` | `src/components/settings/thresholds-editor-section.tsx` | + +These 25 keys were KEPT in all six locales. + +The other **380 keys** were removed from `messages/{de,en,fr,es,it,pl}.json` +in a single sweep via `/tmp/w15-remove-keys.mjs`, which walks each JSON +catalog, deletes the dotted-path entry, and prunes empty parent objects. +Per-locale stats: 380 keys removed, 0 not-found in every locale → the +catalogs were already in parity before this wave (W9d guards that +invariant) and stay in parity after. + +The `i18n-locale-integrity` placeholder allow-list pruned four entries +that no longer exist in the catalogs: +`classifications.bp.Optimal`, `classifications.bp.Normal`, +`classifications.pulse.Normal`, `classifications.bodyFat.Fitness`. +The `targets.{bmi,label.BMI,status.*}` allow-list entries stay — those +keys are still live. + +Top retired namespaces: + +| namespace | removed | rationale | +| --- | --- | --- | +| `settings.*` | 114 | old setup screens, audit-log leaf labels, ai providerChain scaffolding | +| `classifications.*` | 69 | `alerts.*` block + legacy per-metric leaf labels; live UI uses `targets.*` now | +| `admin.*` | 38 | superseded by `admin/[section]` shell | +| `onboarding.*` | 21 | v1.4.22 onboarding rewrite leftovers | +| `medications.*` | 18 | (12 fewer than report — 12 were template-built) | +| `dashboard.*` | 15 | v1.4.20 insights redesign leftovers | +| `bugreport.*` | 12 | simplified bug-report screen | +| `common.*` | 12 | generic strings no longer referenced | +| `charts.*` | 15 | v1.4.20 chart-label slugs (5 fewer — template-built) | +| `auth.*` | 10 | auth UI now uses inline copy | +| `notifications.*` | 10 | toast variants no longer fired | +| `targets.*` | 9 | replaced by `targets.{label,status}.*` | +| `measurements.*` | 7 | stale list-row labels | +| `comparison.*` | 6 | `comparison.deltaVs.*` replaces | +| `mood.*` | 5 | mood-tag descriptors | +| `thresholds.*` | 4 | `thresholds.defaultLabel` kept (live in editor) | +| `achievements.*` | 4 | three more keys cycled out | +| `format.*` | 3 | unused helpers | +| `gettingStarted.*` | 2 | leftover quick-starts | +| `nav.*` | 1 | `nav.charts` | + +Diff stats: each locale lost ~444 lines (380 keys + their JSON +container nesting closures). Total locale-file size: **2,484 lines +removed** across the six files in one atomic commit. + +Spot-check confirmations the brief asked for: + +- **`classifications.alerts.*` (26 keys)** — `git grep -F + 'classifications.alerts' src` returns zero hits. Templated-prefix + patterns under `classifications.alerts.` are also zero (no + `\`classifications.alerts.${…}\`` in code). Safe to remove. +- **`targets.bloodPressure / targets.weight / targets.steps`** etc. + (9 keys) — all zero hits in `src/` and in `prisma/`. The iOS + shadow-key worry from the brief turned out to be unfounded; iOS + adapters use the `targets.label.*` / `targets.status.*` cluster + which is live. + +### Verification + +| step | result | +| --- | --- | +| `pnpm typecheck` | ✓ clean | +| `pnpm lint` | ✓ clean | +| `pnpm vitest run` (full unit) | ✓ 3731 / 3731, 1 skipped | +| `i18n-locale-integrity` | ✓ 26 / 26 (parity still holds) | +| `pnpm vitest run src/app/__tests__/targets-i18n.test.tsx` | ✓ 8 / 8 | + +## Commit 2 — Dead prompt constants (`41c7425b`) + +`BASE_SYSTEM_PROMPT` (in `src/lib/ai/prompts/base-system.ts:196`) and +`INSIGHTS_SYSTEM_PROMPT` (in `src/lib/insights/prompt.ts:106`) were +`@deprecated` since v1.4.20 with zero callers. The Coach surface +migrated to `getBaseSystemPrompt(locale)` + the per-locale +safety-contract matrix (W14c); the Insights surface uses +`getInsightsSystemPrompt(locale)` + per-page-type prompts from the +W3/W6 redesign. The backwards-compatibility re-exports outlived their +purpose. + +Pre-removal `grep -rn 'BASE_SYSTEM_PROMPT[^_]|INSIGHTS_SYSTEM_PROMPT[^_]' src` +returned only the export-site definitions themselves — no consumers. +Post-removal, the same grep returns zero hits. + +This also clears the Cat-C `@deprecated` markers that flagged both +constants in `.planning/research/w10-dead-code-candidates.md` §C1. + +### Verification + +| step | result | +| --- | --- | +| `pnpm typecheck` | ✓ clean | +| `pnpm lint` | ✓ clean | +| `pnpm vitest run src/lib/ai src/lib/insights` | ✓ 1205 / 1205 | + +## Commit 3 — W7d hardening (`61d156e7`) + +Both follow-ups from `.planning/phase-W7d-v1425-dev-server-repair-report.md` ++ `.planning/v1426-backlog.md` §P1-3: + +### `safeRequestProp` narrow-catch + +`src/lib/api-handler.ts` previously caught every exception from the +request-property read so logging instrumentation never crashed the +wrapped handler. A regression in a header parser or a `null` access +inside `read` would have silently fallen back to the default and +hidden the bug. + +The catch is now narrowed to the V8 private-field `TypeError` that +Next 16 raises when the request is a synthetic placeholder on +`force-static` handlers. Three message-shape variants are recognised: + +- `Cannot read private member #state …` (current V8) +- `Cannot read private field #x …` (alternative V8 wording) +- `Cannot read private name #y …` (Bun / older V8) + +Every other `Error` rethrows. A new `__testables` export exposes the +helpers for the unit-test surface. + +### `@source` path resolution + +`src/app/globals.css` now imports Tailwind with an explicit +`source("../..")` base. Tailwind v4 auto-detection otherwise walks +up from the CSS file's resolver root, which can land at an unexpected +level depending on how the build tool reaches the stylesheet +(turbopack vs. webpack vs. test runner). Pinning the base mirrors the +cwd-anchored fix the safety-contracts loader landed in `commit dd44a0d` +so every build-tooling integration point reads paths from the same +root. + +The `@source not "../../.planning"` + `@source not "../../docs"` +directives stay file-relative (Tailwind resolves them from the +stylesheet, not the source-base option) but their resolved targets +remain the same `/.planning` + `/docs` paths. +Source-base anchoring matters for auto-detection, not for the +explicit `not` exclusions. + +### New tests + +`src/lib/__tests__/api-handler-safe-request-prop.test.ts` — 9 tests: + +- `isPrivateFieldAccessError`: the three message wordings classify + match; generic TypeErrors, RangeErrors, bare Errors, and non-Error + values classify non-match. +- `safeRequestProp`: returns the read result on a normal request, + returns the fallback on a private-field TypeError, rethrows any + other exception (downstream parser, undefined-is-not-a-function, + out-of-range). Null-fallback variant covered for header lookups. + +### Verification + +| step | result | +| --- | --- | +| `pnpm typecheck` | ✓ clean | +| `pnpm lint` | ✓ clean | +| `pnpm vitest run src/lib/__tests__/api-handler-safe-request-prop.test.ts` | ✓ 9 / 9 | +| `pnpm vitest run src/lib/__tests__/require-auth-bearer.test.ts` | ✓ 10 / 10 (regression unaffected) | + +## Commit 4 — Cat-C typo polish (`76dea94b`) + +`.planning/research/w10-dead-code-candidates.md` §C2 flagged a comment +at `src/app/api/insights/targets/route.ts:807`: + +``` +// general-status}.ts already enforce this attribution; this +``` + +On re-read this is not a stray-brace typo — it is a brace-expansion +abbreviation `{base-system, general-status}.ts`. The substantive bug +is that the path `src/lib/insights/prompts/` does not exist; the +referenced files actually sit under `src/lib/ai/prompts/`. The +comment is now rewritten in full path form so future readers can +grep the referenced files directly. + +This was the only Cat-C typo entry in the dead-code report (§C1 +items were the prompt-constant `@deprecated` markers, already +cleared by commit 2; §C3 has no findings). + +### Verification + +| step | result | +| --- | --- | +| `pnpm typecheck` | ✓ clean | +| `pnpm lint` | ✓ clean | +| `grep -rn 'general-status}' src` | ✓ zero hits | +| `grep -rn 'insights/prompts/' src` | ✓ zero hits | + +## Numbers + +- **380** dead i18n keys removed from each of six locale files + (= 2 280 catalog entries gone, ~444 file-line deletions per locale). +- **25** keys originally flagged as dead but verified live and kept. +- **5** keys whose only references were in `__tests__/` allow-lists + (4 in `i18n-locale-integrity`, 1 in `sections.test.tsx` — + `settings.export` test asserts the key's *absence*, so the test + is unaffected by removing the catalog entry). +- **2** dead `@deprecated` constants removed. +- **1** narrow-catch helper added with 9 unit tests. +- **1** Tailwind `@source` base pinned to project root. +- **1** comment path rewritten to real file locations. + +## Tests delta + +| suite | before | after | +| --- | --- | --- | +| unit | 3 731 passed, 1 skipped | 3 740 passed, 1 skipped | +| ↳ new in `api-handler-safe-request-prop.test.ts` | — | 9 | +| i18n-locale-integrity | 26 | 26 (parity intact) | + +No integration / Playwright suite touched; no schema / migration +changes; no Coach prompt YAML touched. + +## Touch-disjoint compliance + +| surface | who | mine? | +| --- | --- | --- | +| `messages/*.json` | W15 | yes (exclusive owner) | +| `src/lib/ai/prompts/base-system.ts` | W15 | yes | +| `src/lib/insights/prompt.ts` | W15 | yes | +| `src/lib/api-handler.ts` | W15 | yes | +| `src/app/globals.css` | W15 | yes | +| `src/app/api/insights/targets/route.ts` | W15 | yes (comment only) | +| `src/lib/__tests__/api-handler-safe-request-prop.test.ts` | W15 | yes (new) | +| `src/lib/__tests__/i18n-locale-integrity.test.ts` | W15 | yes (allow-list prune) | + +Parallel agents' work observed in the same window (W18 / W20-rest) +was left untouched — `glp1-medication-card.tsx`, +`maintainership-banner.tsx`, the new `translation.yml` issue template, +the `pickCanonicalSourceRows` simplifier, etc. None of those collide +with this wave's diff. + +## Deferrals + +None. Every Wave-15 scope item landed. Remaining v1.4.26 hygiene +budget left for future waves: + +- P4-3 `coach.batch.too_large` rename already landed in `f16d4b3f` + by a parallel agent. +- P4-4 `detectGlp1Plateau` direct-test coverage already landed in + `c2e5c860` by a parallel agent. +- Other P4-* / P6-* items remain as documented in + `.planning/v1426-backlog.md`.