mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 20:42:43 +00:00
docs(planning): mark Wave-B B3 complete + report
Tick B3 in `.planning/STATE.md` against the six commit drops on `develop` (correlation maths, schema + prompt rule, TrendAnnotation + TrendsRow, CorrelationCard + CorrelationRow, analytics route + page wiring, this report). Replace the stale v1.4.16 host-load report with the v1.4.20 narrative — what shipped, synthetic-data spot checks for each hypothesis, and flagged-uncertain items for maintainer review (confidence-band thresholds, eta-squared mapping, interpretation localisation strategy). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
c83cb2311d
commit
bbd00ca848
2 files changed
+143
-76
No files matched your search
+14
-5
@@ -125,12 +125,21 @@ commit.
|
|||||||
|
|
||||||
### B3 — Correlation discovery + Trends row with AI annotations (~3-5 days)
|
### B3 — Correlation discovery + Trends row with AI annotations (~3-5 days)
|
||||||
|
|
||||||
- [ ] 3 pre-defined hypotheses: BP×compliance, mood×pulse,
|
- [x] 3 pre-defined hypotheses: BP×compliance, mood×pulse,
|
||||||
weight×weekday
|
weight×weekday
|
||||||
- [ ] New `src/lib/insights/correlations.ts`
|
- [x] New `src/lib/insights/correlations.ts` (Pearson + weekday-ANOVA;
|
||||||
- [ ] New `src/components/insights/{correlation-card,trends-row,
|
n >= 14 + p < 0.05 surfacing gate)
|
||||||
trend-annotation}.tsx`
|
- [x] New `src/components/insights/{correlation-card,correlation-row,
|
||||||
- [ ] Confidence ≥ 0.95, n ≥ 14, conservative phrasing
|
trends-row,trend-annotation}.tsx`
|
||||||
|
- [x] Confidence interval via Fisher z-transform; conservative
|
||||||
|
phrasing locked in by code-review convention
|
||||||
|
- [x] PROMPT_VERSION 4.20.0 → 4.20.1; new `trendAnnotations` block
|
||||||
|
on the AI response schema (nullable + optional for legacy
|
||||||
|
payloads)
|
||||||
|
- [x] `/api/analytics` now emits `correlations: {bpCompliance,
|
||||||
|
moodPulse, weightWeekday}` server-side
|
||||||
|
- [x] `<CorrelationRow>` + `<TrendsRow>` mounted on `/insights`
|
||||||
|
between Daily Briefing and the Advisor card
|
||||||
- Detailed report: `.planning/phase-B3-report.md`
|
- Detailed report: `.planning/phase-B3-report.md`
|
||||||
|
|
||||||
### B4 — Weekly Report + Storyboard + Mobile passes (~3-4 days)
|
### B4 — Weekly Report + Storyboard + Mobile passes (~3-4 days)
|
||||||
|
|||||||
+129
-71
@@ -1,89 +1,147 @@
|
|||||||
# Phase B3 — Admin System-Status host-load chart (v1.4.16)
|
# Phase B3 — Correlation discovery + Trends row with AI annotations (v1.4.20)
|
||||||
|
|
||||||
Marc asked for a 2-hour host-load graph above /admin/system-status — CPU,
|
Three pre-defined hypothesis cards (BP × medication compliance, mood ×
|
||||||
memory, disk-IO. Phase ran in Wave-B in parallel with B4/B5a/B7 and the
|
resting pulse, weight × weekday) and an AI-annotated Trends row land
|
||||||
verification gate.
|
on `/insights` between the Daily Briefing and the Advisor card.
|
||||||
|
Six atomic commits on `origin/develop`.
|
||||||
|
|
||||||
## Approach
|
## Approach
|
||||||
|
|
||||||
Picked Option B (in-process sampler) over Option A (Coolify Sentinel
|
Scope discipline locked in early: v1.4.20 ships **3 pre-defined
|
||||||
scrape). Coolify exposes no clean read endpoint for per-server metrics
|
hypotheses**, NOT general automated correlation discovery. Full
|
||||||
without HTML scraping or private RPC; an `os.loadavg()` + `os.totalmem()`
|
metric-pair scan with FDR control is v1.5/v1.6 work.
|
||||||
|
|
||||||
- /proc/diskstats sampler is self-contained, costs one row per minute,
|
Quality bar non-negotiable per the strategic plan:
|
||||||
and works on macOS dev as well as Linux production.
|
|
||||||
|
- `n >= 14` paired data points
|
||||||
|
- `p < 0.05`
|
||||||
|
|
||||||
|
Below the bar each card paints an `<EmptyState>` rather than a
|
||||||
|
near-zero scatter that would imply a pattern that isn't there.
|
||||||
|
|
||||||
## Deliverables
|
## Deliverables
|
||||||
|
|
||||||
1. **`HostMetric` model + migration `0032_host_metric`** —
|
1. **`src/lib/insights/correlations.ts`** — pure runners (`pearson`,
|
||||||
`prisma/schema.prisma`, indexed by `captured_at`. Disk fields nullable
|
`weekdayAnova`, `correlateBpCompliance`, `correlateMoodPulse`,
|
||||||
so non-Linux hosts don't fail.
|
`correlateWeightWeekday`). 95 % CI on r via Fisher z-transform;
|
||||||
2. **Per-minute sampler** — `src/lib/jobs/host-metric-sampler.ts`. Reads
|
eta-squared as the effect-size statistic for the weekday case.
|
||||||
`os.loadavg()`, `os.totalmem() - os.freemem()`, and (Linux)
|
Two-sided p-values from inline Wilson-Hilferty (F) +
|
||||||
/proc/diskstats. Skips loop/ram/dm-/md/sr/fd/nbd/zram pseudo-devices.
|
Abramowitz-Stegun (z) approximations — no new dependencies.
|
||||||
Wired into `reminder-worker.ts` with `* * * * *` cron. 7-day retention
|
16 unit tests pin positive / negative / no-correlation / under-n /
|
||||||
enforced inside the worker; env override `HOST_METRIC_RETENTION_DAYS`
|
non-significant / no-variance branches and the Monday-spike
|
||||||
capped at >= 1 day.
|
weekday flag.
|
||||||
3. **`GET /api/admin/host-metrics?since=2h`** — `requireAdmin()`-gated.
|
|
||||||
Returns `{ samples, meta }`. Server-side BPS derivation across
|
2. **`aiInsightResponseSchema` + prompt rule 9** — added a nullable +
|
||||||
consecutive samples handles counter resets (host reboot) by emitting
|
optional `trendAnnotations: { bp?, weight?, mood? }` block (each
|
||||||
`null` instead of negative deltas. Six `since` presets: 30m / 1h / 2h
|
string ≤ 200 chars). PROMPT_VERSION 4.20.0 → 4.20.1. EN + DE
|
||||||
(default) / 6h / 12h / 24h.
|
ground rule 9 explicitly bans causal language and caps the
|
||||||
4. **`<HostMetricsChart>` component** —
|
sentence length. 13 schema + prompt-shape tests.
|
||||||
`src/components/admin/host-metrics-chart.tsx`. Recharts wrapper with
|
|
||||||
load (yellow, left axis) + memory % (cyan, right axis) + disk-IO %
|
3. **`<TrendAnnotation>` + `<TrendsRow>`** — pure presentational. Row
|
||||||
(purple, right axis, hidden when no disk data). `next/dynamic` import
|
is 1-column on `<md`, 3-up on `>=md`. Annotations show a
|
||||||
from the consumer keeps the Recharts bundle off the rest of the admin
|
`Sparkles` icon + the AI sentence + an optional confidence chip
|
||||||
panel. 60s `refetchInterval` matches sampler cadence.
|
(low / moderate / high). Empty state hints at the gap without
|
||||||
5. **System-status section wiring** — `system-status-section.tsx` now
|
breaking the row's visual rhythm. 15 component tests.
|
||||||
wraps the existing card in a `space-y-6` flex with the chart above.
|
|
||||||
6. **i18n** — 9 EN+DE keys under `admin.hostMetrics.*` (title, load1,
|
4. **`<CorrelationCard>` + `<CorrelationRow>`** — card has tone-coloured
|
||||||
memUsedPercent, diskBusyPercent, diskReadBps, diskWriteBps,
|
left bar, scatter sparkline (deferred-bundled via the existing
|
||||||
last2hours, empty, loadError).
|
`<ScatterCorrelationChart>` wrapper), confidence chip, source-chip
|
||||||
|
("based on N paired readings · last 30 days"), interpretation
|
||||||
|
prose, disabled "Try a 7-day experiment" CTA with `title="Coming
|
||||||
|
soon"`. Row mounts the three cards in 2-up grid on `>=md` and
|
||||||
|
single column on `<md`; the correlation-disclaimer footer
|
||||||
|
("Patterns are observational, not causal — talk to your doctor if
|
||||||
|
BP runs high.") sits below the row once. 13 component tests.
|
||||||
|
|
||||||
|
5. **`/api/analytics` + `/insights` page wiring** — server-side
|
||||||
|
computes the three hypotheses on the trailing 30-day window and
|
||||||
|
surfaces `correlations: {bpCompliance, moodPulse, weightWeekday}`
|
||||||
|
on the response. `<CorrelationRow>` mounts above `<TrendsRow>`
|
||||||
|
between Daily Briefing and the Advisor card. The advisor hook
|
||||||
|
lifts `trendAnnotations` from the cached AI payload through
|
||||||
|
`trendAnnotationsSchema.safeParse` so legacy 4.20.0 payloads
|
||||||
|
resolve to null and trigger the per-metric empty hint. 10
|
||||||
|
source-scan tests pin imports, JSX mounts, and the wide-event
|
||||||
|
annotation.
|
||||||
|
|
||||||
## Tests
|
## Tests
|
||||||
|
|
||||||
- 8 unit tests for the sampler (env retention parsing, disk-stats null
|
- vitest unit suite: 1833 → 1907 (+74 net)
|
||||||
- throw fallbacks, create + deleteMany call shape).
|
- integration suite: 67/67, no regression
|
||||||
- 6 integration tests against testcontainer Postgres for the API
|
- typecheck + lint clean (no new warnings introduced)
|
||||||
endpoint (auth gating, 2h default window, BPS derivation across a
|
|
||||||
counter reset, `?since=30m` preset, empty-DB happy path).
|
|
||||||
- 5 component tests for the chart helper + loading skeleton render.
|
|
||||||
- All 13 unit + 6 integration tests green; i18n parity test green.
|
|
||||||
|
|
||||||
## Cross-agent collisions
|
## Synthetic-data spot checks
|
||||||
|
|
||||||
The verification-gate stash/restore loop in this marathon repeatedly
|
- `correlateBpCompliance({...})` for the canonical 20-row strong-fit
|
||||||
wiped my untracked files (chart component + tests) and hijacked my
|
fixture (compliance 50→97.5 %, systolic 150→120) →
|
||||||
`git commit` invocations so commits 4-6 ended up landing under another
|
`status: "ok"`, `statistic ≈ -1.0` (perfect linear), `pValue < 0.001`,
|
||||||
agent's commit message (`8d9f864 docs(planning): mark Wave-B B5a …`).
|
confidence band label `"high"`, n = 20.
|
||||||
Functionally everything is on origin/main; commit attribution is
|
- `correlateMoodPulse({...})` for the 20-row low-mood/high-pulse
|
||||||
muddled. Commits 1-3 (db, jobs, api) landed cleanly under my titles
|
fixture → `status: "ok"`, negative `statistic`, p well under 0.05.
|
||||||
(`5d1ece1`, `f1bd801`, `2877710`). Commit 3 (`2877710`) is missing the
|
- `correlateWeightWeekday({...})` for the 4-week Monday-spike fixture
|
||||||
Co-Authored-By trailer due to a one-line oversight in my heredoc.
|
(Monday ≈ 84 kg, every other day ≈ 80 kg) → `status: "ok"`,
|
||||||
|
`outlierIndex = 0`, eta² > 0.14 (Cohen "large"), interpretation
|
||||||
|
flags Monday with the conservative "a pattern worth watching"
|
||||||
|
phrasing.
|
||||||
|
|
||||||
Recommendation for v1.4.17: spawn each agent in its own git worktree
|
## Did NOT ship
|
||||||
(`superpowers:using-git-worktrees`) to eliminate the shared-cwd
|
|
||||||
staging race entirely. The current marathon's verification gate keeps
|
|
||||||
collapsing concurrent untracked files into shared stashes, which makes
|
|
||||||
even simple `git add path/to/file && git commit` racy.
|
|
||||||
|
|
||||||
## Files added / modified (final state on origin/main)
|
- **Real "Try a 7-day experiment" scheduling.** The CTA is intentionally
|
||||||
|
disabled in v1.4.20 with the Coming-soon tooltip; live scheduling is
|
||||||
|
a v1.5 surface.
|
||||||
|
- **Storyboard / 90-day BP timeline annotations.** Still B4 work.
|
||||||
|
- **Weekly Report `/insights/report/[week]` route.** Still B4 work.
|
||||||
|
|
||||||
- `prisma/schema.prisma`, `prisma/migrations/0032_host_metric/migration.sql`
|
## Decisions that may want maintainer review
|
||||||
- `src/lib/jobs/host-metric-sampler.ts` + `__tests__/host-metric-sampler.test.ts`
|
|
||||||
- `src/lib/jobs/reminder-worker.ts` (sampler queue registration)
|
|
||||||
- `src/app/api/admin/host-metrics/route.ts`
|
|
||||||
- `tests/integration/admin-host-metrics.test.ts`
|
|
||||||
- `src/components/admin/host-metrics-chart.tsx` + `__tests__/host-metrics-chart.test.tsx`
|
|
||||||
- `src/components/admin/system-status-section.tsx` (wiring)
|
|
||||||
- `messages/en.json`, `messages/de.json` (admin.hostMetrics.\*)
|
|
||||||
- `tests/integration/setup.ts` (truncate `host_metrics`)
|
|
||||||
|
|
||||||
## Did NOT touch (scope guards)
|
- **Confidence-band-from-CI mapping.** `width(CI) <= 0.4` → "high",
|
||||||
|
`<= 0.8` → "moderate", else "low". The thresholds are heuristic; if
|
||||||
|
the user-facing chip ends up too aggressive (e.g. "high" pops on
|
||||||
|
noisy small-n results) we can tighten to 0.3 / 0.6.
|
||||||
|
- **Eta-squared confidence band for weekday.** No closed-form CI on
|
||||||
|
eta² without a non-central F distribution we don't carry. Mapped
|
||||||
|
via Cohen conventions (`eta² >= 0.14` → "high", `>= 0.06` →
|
||||||
|
"moderate") with an n-driven nudge. If marathon QA flags this as
|
||||||
|
too lenient we can drop the nudge or surface "n-low" as its own
|
||||||
|
pseudo-band.
|
||||||
|
- **Interpretation phrasing.** Locked to a small set of conservative
|
||||||
|
templates inside the runners — never reaches the AI provider —
|
||||||
|
so the EN strings live in `correlations.ts` rather than i18n. If
|
||||||
|
Marc wants German-locale interpretations, we'd need to localise
|
||||||
|
these or move them through `t()`.
|
||||||
|
- **Trend-annotation surface gating.** The annotation block is
|
||||||
|
optional — if the model omits a metric, the empty hint surfaces
|
||||||
|
("Awaiting more data to annotate this trend"). For the legacy
|
||||||
|
(4.20.0) cached payload everyone sees the empty hint until they
|
||||||
|
click Regenerate. Opt: we could auto-regenerate once on first
|
||||||
|
page-mount post-deploy; opted not to so users keep their existing
|
||||||
|
cache TTL.
|
||||||
|
- **Inline ANOVA p-value approximation.** Wilson-Hilferty for the
|
||||||
|
F-distribution is good enough for the surfacing decision, but a
|
||||||
|
reviewer should sanity-check against a small-df edge case if Marc
|
||||||
|
is worried about precision near `p ≈ 0.05`.
|
||||||
|
|
||||||
- `src/lib/ai/`, `src/components/insights/`,
|
## Files added / modified
|
||||||
`src/components/charts/scatter-correlation-chart.tsx` (B5a/B5c-e)
|
|
||||||
- `src/components/settings/*` (B2/B6/B7)
|
- `src/lib/insights/correlations.ts` (+
|
||||||
- audit-log section (B4)
|
`__tests__/correlations.test.ts`)
|
||||||
- No new dependencies — Recharts pattern reuse only.
|
- `src/lib/ai/schema.ts` — `trendAnnotationsSchema` + integration
|
||||||
|
- `src/lib/ai/prompts/insight-generator.ts` — PROMPT_VERSION 4.20.1
|
||||||
|
+ GROUND RULE 9 (EN + DE) + JSON shape entry
|
||||||
|
- `src/lib/ai/__tests__/trend-annotations-schema.test.ts`
|
||||||
|
- `src/components/insights/trend-annotation.tsx` +
|
||||||
|
`__tests__/trend-annotation.test.tsx`
|
||||||
|
- `src/components/insights/trends-row.tsx` +
|
||||||
|
`__tests__/trends-row.test.tsx`
|
||||||
|
- `src/components/insights/correlation-card.tsx` +
|
||||||
|
`__tests__/correlation-card.test.tsx`
|
||||||
|
- `src/components/insights/correlation-row.tsx` +
|
||||||
|
`__tests__/correlation-row.test.tsx`
|
||||||
|
- `src/app/api/analytics/route.ts` — three runners + Berlin-day
|
||||||
|
bucketing helpers
|
||||||
|
- `src/app/insights/page.tsx` — mounts CorrelationRow + TrendsRow
|
||||||
|
- `src/components/insights/use-insights-advisor.ts` — lifts
|
||||||
|
trendAnnotations from the cached payload
|
||||||
|
- `src/app/__tests__/insights-b3-wiring.test.ts`
|
||||||
|
- `messages/en.json`, `messages/de.json` —
|
||||||
|
`insights.{trendsRow,trendAnnotation,correlationRow}` keys
|
||||||
Reference in new issue
Block a user