feat: serve the day, its index, the timeline and life events

Replace the day, timeline and life-event stubs with their implementations
(#613) and drop the not-implemented helper, its error code and its OpenAPI
response with the last stub.

loadDay reads one local day for every reader: the readings live inside the
record's own day window (never the UTC rollups), through the canonical-source
engine the mood sheet already uses (resolveDayReadings in linked-context.ts),
with the person's usual range over the 30 days before; the record sections
from their own tables, each in the storage form it speaks (an instant in the
local window, a date string with its own zone, a calendar date). A switched-off
module's section is not read; a section the grant does not cover is named
not_shared and not read.

The day, the day index and the new visit preparation (GET /api/day/notable)
are admitted on the readings and narrowed per section through
actingDomainVisibility, so a scoped grant sees exactly its sections. The
timeline and its readiness stay whole-record reads and narrow their lanes the
same way.

Notable days are deterministic keys: a three-month high or low with history
behind it, a first reading, and in the visit preparation a regular reading
that stopped. The timeline lays out spans and points from the owning tables,
standing items without a date, and series by zoom: months from the rollup
tier inside its five-year window and from one cached live query before it.
The readiness inventory rates each lane with one link per gap.

Life events are created, edited and soft-deleted as MANAGE verbs with audit
rows, encrypted at rest, and join the sharing guard and the handler matrix.
get_day reads a day for the Coach and the MCP endpoint without life events,
notes or document names; its step carries the date it read.
This commit is contained in:
Marc-André Bombeck committed 2026-10-08 14:31:13 +02:00
1 parent b8f6372963
commit 0924285cd6
65 files changed
+7642 -209

No files matched your search

+3 -1
View File
@@ -118,6 +118,7 @@ export const ERROR_CODE_CATALOGUE: Readonly<Record<string, readonly string[]>> =
],
"cycle-prefs": ["cycle-prefs.body.invalid_json", "cycle-prefs.invalid"],
"disable-coach": ["disable-coach.body.invalid_json"],
day: ["day.invalid"],
demo: ["demo.readOnly"],
documents: [
"documents.chat.conversationNotFound",
@@ -230,6 +231,7 @@ export const ERROR_CODE_CATALOGUE: Readonly<Record<string, readonly string[]>> =
"labs.update.referenceRangeInvalid",
"labs.update.sourceReferenceRangeInvalid",
],
life_event: ["life_event.invalid", "life_event.not_found"],
managed_profile: [
"managed_profile.guardian.duplicate",
"managed_profile.guardian.managed_invitee",
@@ -380,6 +382,7 @@ export const ERROR_CODE_CATALOGUE: Readonly<Record<string, readonly string[]>> =
"symptoms.event.notFound",
"symptoms.invalid",
],
timeline: ["timeline.invalid"],
tokens: [
"tokens.documents.ceiling_reached",
"tokens.measurements.ceiling_reached",
@@ -423,7 +426,6 @@ export const ERROR_CODE_CATALOGUE: Readonly<Record<string, readonly string[]>> =
"modules_conflict",
"mood_tag_layout_conflict",
"not_configured",
"not_implemented",
"notification_prefs_conflict",
"oidc_only",
"private_origin_not_approved",