mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-12 06:06:10 +00:00
feat(topics): compute topic overviews and topic pages on the server
GET /api/topics answers one entry per topic the record holds data for:
a headline value, a rule-based sentence and a 30-day sparkline.
GET /api/topics/{topic} answers a topic's signals with their usual
ranges, side-by-side series for a chosen window, the relationships
between its signals, 90-day trends, what it holds nothing for yet, and
its blocks (training load, lab markers with their trend, screener
trends, the cycle day).
Every figure comes from reads the metric pages already make: the local
daily values, the reconstructed nights, the mood score, computeLabTrend,
the training load and the screener trends. No model is involved. A
relationship says nothing below 14 paired days and is only called a
pattern at p < 0.05; a lab trend needs three readings spanning a year.
The preview is the viewer's own opt-in: with it off both routes answer
404 topics.preview_off. Topics, signals and blocks follow the record's
module switches and the sections the viewer's grant opens; the routes
read across sections, so a scoped grant is refused like the timeline.
/api/auth/me publishes the caller's opt-ins as previewFeatures.
This commit is contained in:
1 parent
14d0184b63
commit
cc04e0944d
30 files changed
+3937
-35
No files matched your search
@@ -1074,6 +1074,17 @@ const DELEGABLE_ROUTES: Record<string, DelegableEntry> = {
|
||||
domain: "record",
|
||||
why: "One search over the record: hits from every section with an in-app path each. Read-only, scoped to the resolved user, every source narrowed to the record's module switches and to the sections the grant covers, the query never logged.",
|
||||
},
|
||||
// The topic pages (preview) lay several sections side by side, like the
|
||||
// timeline, so they stay whole-record reads. The preview switch they check
|
||||
// is the ACTOR's own row and decides nothing about the record.
|
||||
"app/api/topics/route.ts": {
|
||||
domain: "record",
|
||||
why: "The topic overview: a headline value, a rule-based sentence and a sparkline per topic, computed from the record's daily values, lab markers, screener totals and cycle starts. Read-only, no model, scoped to the resolved user, every signal and block narrowed to the record's module switches and to the sections the grant covers.",
|
||||
},
|
||||
"app/api/topics/[topic]/route.ts": {
|
||||
domain: "record",
|
||||
why: "One topic page: signals with usual ranges, side-by-side series, relationships, trends and the topic's blocks. Read-only, no model, scoped to the resolved user, narrowed per signal and block like the overview.",
|
||||
},
|
||||
"app/api/workouts/route.ts": {
|
||||
domain: "measurements",
|
||||
why: "The record's workout list. Verified to carry no AI-written paragraph: the single-workout route beside it does, and stays refused for exactly that reason.",
|
||||
@@ -2197,8 +2208,11 @@ const ACTOR_ROUTES: Record<string, string> = {
|
||||
* side). The activity goals route does not: setting or changing goals stays
|
||||
* owner-only, and a delegate sees the goals only as the calendar reports them.
|
||||
* 270 -> 273.
|
||||
*
|
||||
* The topic overview and the topic page (preview) join the record list as
|
||||
* whole-record reads, like the timeline. 273 -> 275.
|
||||
*/
|
||||
const FROZEN_ENTRY_COUNT = 273;
|
||||
const FROZEN_ENTRY_COUNT = 275;
|
||||
|
||||
/**
|
||||
* The two surfaces that authenticate a Bearer token outside `requireAuth` —
|
||||
|
||||
Reference in new issue
Block a user