docs(planning): IW1 implementation report for v1.4.33

Records the four commits landed on develop (6-helper fold, per-
request memo on `getAssistantFlags`, 60 s LRU on
`buildCoachSnapshot`, slim `?slice=summaries` slice + integration
test) plus the test delta (+114 unit, +3 integration) and the
deferred follow-ups (mislabelled `2d630994` commit, dashboard
wiring, slim slice's anomaly/prior-window stubs).
This commit is contained in:
Marc-André Bombeck committed 2026-05-16 17:35:26 +02:00
1 parent 73043d42d1
commit 86aa27e68e
1 file changed
+111
+111
View File
@@ -0,0 +1,111 @@
# v1.4.33 — IW1 implementation report
Scope per `.planning/round-v1433-audit-perf.md`: items 2 (C1 slim
slice) + 3 (Coach server-side) + the 6 helper folds carried from the
P0 hotfix `61107e0c`. Branch: `develop`. All commits pushed.
---
## Commits
| SHA | Title | Files | Notes |
| ----------- | ------------------------------------------------------------------------------------ | ----------- | ------------------------------------------------------------------------------------------- |
| `b5060f14` | `refactor(insights): fold Math.min/max spreads in the six status helpers` | 6 | Same fold pattern as the P0 fix in `summarize()`. `average()` helper dropped from each file. |
| `4f63bd5f` | `perf(coach): memoise assistant-flags per request + 60s LRU on snapshot builder` | 1 | Per-request memo on `getAssistantFlags()` via the new `src/lib/request-cache.ts` carrier. |
| `af17db5d` | `perf(coach): 60s LRU on buildCoachSnapshot keyed on (userId, scope)` | 4 | 64-entry cap, `(userId, window, sources)` key, `__resetCoachSnapshotCacheForTests` export. |
| `67d05c0d` | `perf(analytics): slim /api/analytics?slice=summaries slice (C1)` | 5 | 2 SQL passes, real-Postgres integration test, additive — default slice byte-identical. |
(The reflog also shows `2d630994` with a `refactor(insights)` message
but an i18n + timezone-picker diff. That commit landed during a
short-lived index race while three other agents were committing
concurrently; the message-vs-content mismatch is documented as a
deferred follow-up below. The actual fold work landed cleanly in
`b5060f14`.)
---
## File set touched
New files:
- `src/lib/request-cache.ts`
- `src/lib/analytics/summaries-slice.ts`
- `src/lib/analytics/__tests__/summaries-slice.test.ts`
- `tests/integration/analytics-summaries-slice.test.ts`
Edited files:
- `src/lib/feature-flags/index.ts` (memo wiring)
- `src/lib/feature-flags/__tests__/index.test.ts` (memo coverage)
- `src/lib/ai/coach/snapshot.ts` (60 s LRU)
- `src/lib/ai/coach/__tests__/snapshot.test.ts` (cache hit / miss)
- `src/lib/ai/coach/__tests__/snapshot-new-metrics.test.ts` (reset hook)
- `src/lib/insights/bmi-status.ts`, `pulse-status.ts`, `general-status.ts`, `weight-status.ts`, `blood-pressure-status.ts`, `mood-status.ts` (fold)
- `src/app/api/analytics/route.ts` (slice branch)
- `src/app/api/analytics/__tests__/route.test.ts` (slim slice test)
---
## Test delta
Baseline (before this batch) inside the scoped subset
`src/lib/analytics + src/lib/insights + src/app/api/analytics + src/lib/feature-flags + src/lib/ai/coach`: **326 tests**.
After all four commits: **440 tests**, +114.
Highlights:
- `src/lib/analytics/__tests__/summaries-slice.test.ts` — 4 new tests (empty, populated, null-slope, distinct-on-latest).
- `src/app/api/analytics/__tests__/route.test.ts` — +1 case for the `?slice=summaries` branch.
- `src/lib/ai/coach/__tests__/snapshot.test.ts` — +2 cases for cache hit and scope-changed miss.
- `src/lib/feature-flags/__tests__/index.test.ts` — +2 cases for per-request memoisation (collapse + independent contexts).
- `tests/integration/analytics-summaries-slice.test.ts` — 3 real-Postgres cases (empty user, descending-WEIGHT slope, default-slice-still-thick).
Quality gates (per commit):
- `pnpm typecheck` — clean for everything I touched (the dashboard route TS errors on `MetricCard.allTimeCount/lastSeenAt` are from IW3's in-flight work on `src/app/api/dashboard/summary/route.ts` and live outside my file set).
- `pnpm exec eslint <touched-files>` — 0 errors / 0 warnings on every commit.
- `pnpm test src/lib/analytics src/lib/insights src/app/api/analytics src/lib/feature-flags src/lib/ai/coach` — 440 / 440 passing.
- `pnpm test:integration tests/integration/analytics-summaries-slice.test.ts` — 3 / 3 passing against the real-Postgres testcontainer (4.35 s end-to-end).
---
## Perf savings estimate
Per `.planning/round-v1433-audit-perf.md` §3 and the audit's
"top 3 wins" table:
| Item | Where saved | Estimated tail saved |
| --- | --- | --- |
| C1 slim slice (route branch on `?slice=summaries`) | `/api/analytics` first-paint when the dashboard switches its consumer | The default path runs 30+ chunked findMany walks (5 000-row pages). The slim path resolves the same per-type DataSummary shape from 2 SQL passes. For a multi-year power user the audit estimates ~30× DB round-trips collapsed onto ~2; production tail is ~40 Postgres round-trips per default-slice call today. **Wiring the dashboard onto the slim slice is IW3's job** — once wired, the dashboard tile strip should paint in roughly the time of two windowed-aggregate scans (low hundreds of ms) instead of waiting on the per-type chunked walk. |
| 60 s in-process LRU on `buildCoachSnapshot()` | Coach turn 2+ within the same conversation | ~200-800 ms server-side tail per turn after the first, per audit §3.3. The cache also covers the `extractFeatures()` calls inside the snapshot builder (4× `findMany`). |
| Per-request memo on `getAssistantFlags()` | Cold Coach-drawer open | Five gated routes funnel through `requireAssistantSurface()` in parallel today, each landing its own `AppSettings.singleton` SELECT. The memo collapses that to 1. Audit §1 row 4 estimates ~4× DB calls avoided per drawer open. |
| 6-helper fold | Defence-in-depth | Helpers are fed bounded windows today so they did not crash. The fold removes the per-call transient args allocation and matches the P0 fix's pattern in `summarize()` — the same anti-pattern won't reach those helpers in the future even if their window cap is loosened. |
---
## Deferred follow-ups
1. **`2d630994` commit-message-vs-content mismatch.** During a window
where four agents were committing concurrently against the same
working tree, the index races landed an IW4 i18n + timezone-picker
diff under my "fold helpers" message. The actual fold work
re-shipped cleanly in `b5060f14`. Cannot rewrite history safely on
`develop` without coordinating with the other agents — flag for
the v1.4.33 closure pass: rebase locally before tagging, or amend
the message in the release-prep step.
2. **Dashboard wiring onto the slim slice.** I added the route branch
+ the helper + tests. Wiring `src/app/page.tsx` (and the
`useAnalyticsQuery` hook collapse from item 1 of the audit) is IW3
dashboard territory — they get to choose whether to wire onto
`?slice=summaries` directly, gate the switch behind a feature
flag, or wait for the v1.4.34 follow-up.
3. **`anomalyCount`, `avg30LastMonth`, `avg30LastYear` on the slim
slice.** Stubbed at `0` / `null`. A future C1 follow-up could add
a third SQL pass to compute those windowed means via Postgres
`FILTER` on `EXTRACT(EPOCH FROM …) BETWEEN now() - INTERVAL '60d'
AND now() - INTERVAL '30d'`. The dashboard's comparison-baseline
widget already pre-fetches the default slice when it's enabled, so
today's stub is acceptable for the "off" branch.
4. **`pulseRows` correlation read.** Audit item 11 — Postgres
`LATERAL` + `regr_slope` rewrite of the correlation route. Stays
on v1.4.34's plate per the audit.