mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 20:42:43 +00:00
docs(planning): W15 hygiene cluster phase report
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 removed61d156e7— safeRequestProp narrow-catch + Tailwind @source base76dea94b— Cat-C comment path rewrite on /api/insights/targets
This commit is contained in:
1 parent
76dea94bae
commit
4ab0d17808
1 file changed
+296
@@ -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 `<repo-root>/.planning` + `<repo-root>/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`.
|
||||
Reference in new issue
Block a user