feat(insights): add tag-by-metric crosstab and deprecate the moodLog webhook

Extend the mood-relations surface from mood to a health metric. For each
structured mood tag present on enough days, compare a metric's value on
tag-present vs tag-absent days: active energy on workout days (same day),
sleep length on nights tagged sleep (same day), and next-day recovery
after a tag like alcohol or food (D to D+1 lag). The comparison reuses the
existing statistics engine wholesale — the Welch two-sample t-test, the
per-group day floors, the confidence band, and the Benjamini-Hochberg FDR
control already used by the influence and discovery boards — so no new
statistics code lands. Only pairs that clear p < 0.05 and the FDR control
surface, ranked by adjusted q then absolute delta and capped at eight rows.

The result rides on GET /api/mood/insights as a new additive
`tagMetricCrosstab` field and renders as a confidence-gated, observational
card on the mood insights page, framed as an association in your own data
rather than a cause. The endpoint is server-rendered and not in the OpenAPI
contract, so no contract change is required.

Deprecate the standalone moodLog integration. Mood is now tracked fully
inside HealthLog through entries, structured tags, and rated factors, so
the external webhook and reverse-sync bridge no longer add anything. The
webhook route, the push and pull helpers, and the validation schemas carry
an @deprecated note pointing at the native path; the surface stays
functional this release and is slated for removal in a future major. The
settings card shows a deprecation hint, localised across all six locales.
This commit is contained in:
Marc-André Bombeck committed 2026-06-05 00:33:15 +02:00
1 parent 65b00e3a73
commit cfb97593a3
19 files changed
+971 -1

No files matched your search

@@ -12,6 +12,13 @@ import { recomputeMoodBucketsForEntry } from "@/lib/rollups/mood-rollups";
export const dynamic = "force-dynamic";
/**
* @deprecated The standalone moodLog webhook is superseded by native mood
* entries plus structured tags and rated factors — mood is now tracked
* fully inside HealthLog, so the external moodLog bridge no longer adds
* anything. Kept functional for existing integrations; slated for removal
* in a future major release. Do not build new callers against it.
*/
export const POST = apiHandler(async (request: NextRequest) => {
annotate({ action: { name: "moodlog.webhook" } });