docs(planning): mark Wave-B B4 complete + report

Five-commit Phase B4 lands the printable weekly report route, the
hero banner card surfacing fresh weekly reports, additive storyboard
annotations on the 90-day BP chart, and mobile rail trays for the AI
Coach drawer. PROMPT_VERSION 4.20.1 -> 4.20.2 with new GROUND RULES
10/11 covering both new schema blocks.

Tests 1902 -> 1975 (+73). Typecheck + lint clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Marc-André BombeckandClaude Opus 4.7 committed 2026-05-10 17:24:59 +02:00
1 parent ebb2bc0602
commit 54d3e92acc
2 files changed
+163 -73

No files matched your search

+17 -4
View File
@@ -144,10 +144,23 @@ commit.
### B4 — Weekly Report + Storyboard + Mobile passes (~3-4 days)
- [ ] New `/insights/report/[week]` route + `window.print()` export
- [ ] Storyboard 90-day BP timeline with annotations[]
- [ ] Mobile passes: B1 + B2 mobile equivalents (drawer →
full-screen sheet)
- [x] PROMPT_VERSION 4.20.1 → 4.20.2; aiInsightResponseSchema gains
`weeklyReport` + `storyboardAnnotations`; GROUND RULES 10/11
across EN + DE prompts
- [x] New `/insights/report/[week]` route — newsletter-style
printable surface with Summary / Going-well / Worth-watching /
Tips / Data-quality sections; `window.print()` export via
Tailwind `print:` variants
- [x] Hero strip banner-card surfaces the fresh weeklyReport with
Read · Share · Export PDF actions; Share uses Web Share API +
clipboard fallback; Export deep-links to `?print=1`
- [x] `<HealthChart>` gains an additive `annotations[]` prop;
`resolveAnnotationPositions()` pure helper maps date→pointIndex
with a 7-day snap window and 24-char small-viewport truncation
- [x] Coach drawer rail trays — chevron-button triggers on `<lg`
open side-sheets that host the same HistoryRail / SourcesRail
the desktop layout uses; refactored body into
`<CoachDrawerBody>` for SSR-test surface
- Detailed report: `.planning/phase-B4-report.md`
### B5 — Personal AI Coach Health Score (~2-3 days)
+146 -69
View File
@@ -1,89 +1,166 @@
# Phase B4 — Admin logs visibility deepening (v1.4.16)
# Phase B4 — Weekly Report + Storyboard + Mobile passes (v1.4.20)
Status: complete
Date: 2026-05-10T00:11+02:00
Commits on origin/main:
Date: 2026-05-10
Branch: `develop`
- `8ac5602` (audit-log filtering, pagination, CSV export — bundled with
the Wave-A verification-gate planning commit due to the marathon
coordinator's stash race; my code, wrong commit subject)
- `4cc3d8d feat(admin): app-log preview surfaces last 1h of structured events with filter + JSON inspector`
- `6520ae4 feat(admin): sidebar entry for app-logs`
## Scope
## Scope delivered
Continues the Insights redesign with the printable weekly report
route, AI-narrated storyboard annotations on the 90-day BP chart,
and mobile passes for the B1 hero + B2 Coach drawer. Strategic
plan: `.planning/phase-D-v1419-product-lead-review.md` § "Phase B4 —
Weekly Report + Storyboard + Mobile passes".
### 1. Audit-log enhanced filtering, pagination, CSV export
## Commits on `origin/develop`
`<LoginOverviewSection>` is now the deeper viewer admins asked for. It
keeps the failed-only quick-pill from the v1.4.x UI and stacks the new
filter row on top: actor (matches userId OR `user.username`,
case-insensitive), action (dropdown populated from
`/api/admin/audit-log/actions`'s `groupBy(action)`), target (substring
on the JSON-encoded `details` column), and date-range presets
(24h / 7d / 30d / all). Per-page is 25 / 50 / 100; next/prev pagination
is real, total comes from a `count()` companion query. CSV export uses
the existing `toCSV()` helper and downloads what the active filter
set returns right now.
1. `6d4cba1` feat(insights): weeklyReport + storyboardAnnotations schema + prompt
2. `8f18c7f` feat(insights): /insights/report/[week] printable weekly report
3. `f186845` feat(insights): hero strip weekly-report banner card
4. `ff1e335` feat(charts): storyboard annotations on HealthChart
5. `ebb2bc0` feat(coach): mobile rail trays for the AI Coach drawer
6. (this commit) docs(planning): mark B4 complete + report
`GET /api/admin/audit-log` learned the `actor` / `action` / `target` /
`since` / `until` / `page` / `perPage` query params. Out-of-range
`perPage` falls back to 50 instead of 400-ing so a stale UI never
deadlocks. Legacy `limit` / `offset` / `filter=auth` callers
(`<RecentAuditPreview>`, the failed-only pill) are unchanged.
## What shipped
9 unit tests pin every filter and the pagination contract; 1 covers
the actions endpoint.
### 1 · Schema + prompt (commit 1)
### 2. App-log preview surfaces last 1h of structured wide-events
- `weeklyReportSchema` — ISO week + summary (10..800 chars) +
goingWell / worthWatching / tips arrays (≤5 entries each, every
bullet ≤280 chars) + optional dataQualityNotes (≤280 chars).
- `storyboardAnnotationSchema` — date (YYYY-MM-DD) + label (≤80
chars) + category (medication / event / milestone / warning) +
detail (≤400 chars). Hard array cap 20.
- Both blocks are `nullable().optional()` on the response root so
legacy 4.20.1 caches round-trip.
- PROMPT_VERSION bumped 4.20.1 → 4.20.2.
- GROUND RULE 10 + 11 added to EN + DE prompts with conservative
phrasing constraints + section-name match.
New `src/lib/logging/in-memory-buffer.ts` — 500-entry FIFO ring
buffer, hooked into `transports.emitEvent()`. Mirrors what would ship
to Loki (sampler-gated). Per-process and volatile — header copy
spells that out so admins know to keep Loki configured for durable
diagnostics.
### 2 · `/insights/report/[week]` (commit 2)
`GET /api/admin/app-logs` reads the buffer with `traceId` / `level` /
`action` / `since` / `until` / `limit` filters, runs every event
through `redactSecrets()` on egress so a stray Bearer / hlk\_ / sk-
token in `error.message` never leaks to the admin UI; storage stays
raw to keep the diagnostic value when shipped to Loki.
- New `src/lib/insights/week-iso.ts` with `parseWeekISO`,
`toWeekISO`, and `weekISOToRange`. Pure helpers, full unit suite.
- New `src/components/insights/weekly-report-view.tsx` split into
`<WeeklyReportView>` (consumes advisor query + auth) and
`<WeeklyReportPresentation>` (pure layout). The split keeps the
vitest tests free of TanStack Query setup.
- New `src/app/insights/report/[week]/page.tsx` — thin client
wrapper. Malformed slug → 404 via `notFound()`. `?print=1` query
triggers `window.print()` after first paint.
- Print export uses Tailwind `print:` variants: toolbar hidden,
article width unconstrained, padding zeroed. A4 / Letter both
print without margin clipping.
- Empty state with Generate CTA when the cached report's weekISO
doesn't match the route param.
- EN + DE i18n keys under `insights.report.*`.
`<AppLogPreviewSection>` lives at the new `/admin/app-logs` route —
table of recent events (level icon / timestamp / action / duration /
trace short-id), click row → JSON-pretty-print modal, 30s refetch.
Mirrors the per-section pattern v1.4.16 A1 established.
### 3 · Hero banner card (commit 3)
### 3. Sidebar entry
- New `weeklyReportReady?: { weekISO, href }` prop on
`<HeroStrip>`. When set, the hero paints a slim banner card with
Sparkles icon, "Your Week N report is ready" label, and Read /
Share / Export PDF actions.
- Read → in-app `<Link>` to the report URL.
- Share → `navigator.share()` when supported, else
`navigator.clipboard.writeText` + sonner toast acknowledgement
(success or failure).
- Export PDF → deep-links to the report URL with `?print=1` so the
report page auto-fires `window.print()` after first paint.
- The insights page derives `weeklyReportReady` from
`advisor.payload.insights.weeklyReport` so the banner appears
the moment a fresh PROMPT_VERSION 4.20.2 generation lands.
- EN + DE i18n keys under `insights.heroBanner.*`.
`<AdminShell>` gains the `app-logs` row with a `FileText` icon. The
global sidebar still surfaces a single `/admin` link per the v1.4.15
A1 + v1.4.16 A1 conditional pattern — per-section nav stays inside
the shell.
### 4 · Storyboard annotations on `<HealthChart>` (commit 4)
### 4. i18n
- Additive `annotations?: Array<{ date, label, color }>` prop on
`<HealthChart>`. The chart line + tooltip + comparison overlay
remain untouched — annotations sit between the cartesian grid
and the data lines so they read as orientation, not data.
- New pure helper `resolveAnnotationPositions()` maps annotation
dates onto the bucketed point indices with a 7-day snap window
(so annotations don't stack on monthly buckets) and a 24-char
truncation precomputed for `<sm` rendering. Full unit suite.
- The `/insights` page wires advisor.payload.insights.
storyboardAnnotations into the 90-day BP chart, mapping the four
canonical categories to Dracula colours (medication=pink,
event=cyan, milestone=green, warning=orange).
- Recharts visual identity preserved per
`feedback_charts_visual_identity.md` — the additive
`<ReferenceLine>` does NOT change the existing line/area visuals.
`admin.section.auditLog.*` (filterActor, filterAction,
filterActionAll, filterTarget, filterSeverity, filterDate, perPage,
prev / next / pageOf, export, empty + range24h / 7d / 30d / all) and
`admin.section.app-logs.*` (subtitle, processNote, filterTraceId /
Action / Level / Range, range15m / 1h / 6h / all, table column
labels, empty + emptyDescription, refresh, eventDetails, closeDetails)
land in both EN and DE. Locale-integrity guard stays green.
### 5 · Mobile rail trays (commit 5)
## Coordination friction
- The history rail (left) and sources rail (right) were desktop-only
since B2b. B4 surfaces them on `<lg` via two chevron-button
triggers anchored to the inside edges of the message thread.
- Tapping a trigger opens a side `<Sheet>` that hosts the same
HistoryRail / SourcesRail instance the desktop layout uses.
- Refactored the drawer body into a separate
`<CoachDrawerBody>` component — pure-presentational shell,
parent owns state. The split lets the SSR test harness pin the
trigger slot markers without rendering the outer Radix Sheet
portal (which is client-only).
- Selecting a conversation from the mobile history tray closes the
tray automatically so the user lands back on the message thread.
The marathon coordinator stashed my work-in-progress 6 times across
the session ("wave-a-gate-stash" through "wave-a-gate-stash-final")
and re-committed parts of it under foreign agents' commit messages —
which is why commit 1's logic landed under `8ac5602 docs(planning):
Wave-A verification gate report + STATE update`. Code is verbatim
what B4 wrote; only the commit subject drifted. Re-recommends the
per-agent worktree adoption already noted as recurring meta in
STATE.md from v1.4.15.
## Verification gates
## Verification
- `pnpm typecheck` — clean.
- `pnpm lint` — 13 pre-existing warnings (none introduced by B4).
- `pnpm test --run` — 1902 → 1975 tests (+73). Test files 230 → 235.
- `pnpm test:integration` — not regressed (no integration coverage
added in B4 scope; lift to v1.4.21 if/when the report route grows
a server endpoint).
- `pnpm vitest run src/lib/logging/ src/app/api/admin/audit-log/ src/app/api/admin/app-logs/ src/components/admin/__tests__/sections.test.tsx` — 51 / 51 green
- `pnpm typecheck` — 0 B4 errors (the typecheck failures present are
sibling B7 broken endpoint paths)
- `pnpm lint` — 0 errors / 12 pre-existing warnings (none on B4 files)
## Print verification
`window.print()` produces a clean A4 / Letter print. The toolbar
hides via `print:hidden`, the article wrapper drops padding via
`print:px-0 print:py-0` and uses `print:max-w-none` so the report
fills the page without margin clipping. Each section paints as a
borderless block via `print:border-0 print:rounded-none`. JsPDF
parity (matching the doctor-report style) remains v1.5 work as
scoped.
## Memory-care
- `feedback_charts_visual_identity.md` — Recharts kept; the
annotation rendering uses Recharts' built-in `<ReferenceLine>`
primitive without touching the existing chart line / area /
tooltip visuals.
- `feedback_marc_voice_english.md` — every commit message + UI
string + report sentence is in Marc's voice; no maintainer-name
references in code or commits.
## Deferred / not shipped
- jsPDF parity for the weekly report (matches doctor-report style)
→ v1.5 once Apple Health adds richer data per the strategic
plan.
- A weekly cron job that auto-generates the weekly report at the
end of each ISO week — current model: the report is part of the
rolling AI advisor payload, surfaces when a regenerate produces
one. Future work could schedule it via pg-boss.
- Mobile pass for B1 (HeroStrip + DailyBriefing) — the components
already responded to 375 px from B1 ship; no layout regressions
spotted at the B4 review. Banner card on the hero stacks
cleanly inside the existing `flex-wrap` action row.
## UX decisions worth flagging for review
1. **Banner card placement** sits between the title block and the
action row, not above the title (which would push the greeting
below the fold on 375 px). Chosen so the banner reads as
"secondary callout" without competing with the greeting.
2. **Share fallback** uses sonner toasts rather than a modal —
matches the rest of the app's lightweight feedback pattern.
3. **Storyboard annotation snap window** is 7 days. Wider snaps
(e.g. 14 days) caused multiple annotations to collapse onto the
same monthly bucket on the 90-day window; 7 days reads cleanly
on every visible bucket type without dropping legitimate events.
4. **Mobile trays use the same rail components** as the desktop
layout. The alternative was to render compact-mobile variants;
keeping a single source of truth means future polish to the
rails lands on both surfaces simultaneously.