الصفحات · Health Debug
M5 — Medication Grace — Session Card
Planning artifact. A future build session opens this to know exactly what to build for module M5 Medication Grace, in what order, with what dependencies and done-criteria. No production code here. Read alongside
docs/plan/13-execution-modules-and-sessions.md(the session framework this card implements),03-protocol-engines.md §5(the engine spec),04-backend-togo.md(schema/migration/plugin rules), and the relevant platform PRD per surface.
Purpose
Medication Grace (Protocol Engine 5) gives users a humane, flexible window to log scheduled medications and tracks adherence-to-logging — never anything about the drugs themselves. The engine is a logging clock: it stores user-entered medication names and schedules verbatim, opens a 60-minute flexible grace window per scheduled dose, and records each dose as Logged / Missed / LateLogged. Per Core Opinion 2 it performs no drug-category inference, no dosage validation, and no interaction checking — a drug_safety_facts table exists only as an empty seam reserved for a future licensed provider and stays empty in this build. Because all temporal/state logic lives server-side, this module also closes legacy divergence #1 (Meds & Cycle missing on iOS) purely by having each client consume the API — no client re-implements the grace window.
Engine parameters (exact — do not invent)
| Parameter | Value | Source |
|---|---|---|
| Grace window | 60 minutes, flexible, per scheduled dose | 03 §5.2 |
| Window model | abstract [window_open, window_close] spanning 60 minutes around/after the scheduled dose time | 03 §5.2 |
| Per-dose states | Scheduled → GraceOpen → Logged | GraceOpen → Missed → LateLogged (late is flagged, never hidden) | 03 §5.3 |
| Schedule kinds | fixed_times / interval / as_needed (PRN has no scheduled dose ⇒ no grace window, no overdue) | module brief |
| Overdue handling | advisory only; a skipped dose counts as kept (adherence-to-logging), never a health judgment | legacy precedent, module brief |
| Drug reasoning | prohibited — no name→drug-class mapping, no dosage validation, no interaction checks | 03 §5.4, Core Opinion 2 |
| Duplicate dose log | second log for the same dose window rejected as duplicate, kept in audit trail | 03 §5.7 |
| Fail-safe | when the engine cannot determine which scheduled dose a log belongs to, it stores the log unattached and asks the user, never guesses | 03 §5.7 |
| Medical deferral | every medication-related uncertainty terminates in "Consult your doctor." | 03 §5.4, Core Opinion 2 |
Reference REST surface (names only; the backend session owns final /api/v1/ paths — clients conform to Phase 1, per 04 §7):
POST /v1/medications (create schedule) · GET /v1/medications (list) · PUT /v1/medications/{id} (update) · POST /v1/engines/medgrace/doses/{id}/log (log within/after grace) · GET /v1/engines/medgrace/state (today's dose windows + states). (Note: platform PRD sketches say /api/v1/medications/entries (web) and /v1/engines/medication/log (android); these are consumption sketches — the backend session normalizes the canonical paths and every client conforms.)
Sessions
Surface order within the engine (13 §3): backend → web → mcp → chrome → apple → android → desktop. The backend session must land before any client session. <date> and NN are placeholders filled at session start; Mx = M5.
| Session ID | Surface | Scope | Entry deps | Done criteria (13 §5 template) | Plan docs to read |
|---|---|---|---|---|---|
S-<date>-M5-backend-NN | backend | medicationgrace engine as its own ToGO plugin: pure core/ state machine (60-min grace window; Scheduled/GraceOpen/Logged/Missed/LateLogged; overdue-advisory-only; skipped-counts-as-kept) + plugin.go registration + expand-contract migration for medications (user-entered name verbatim), schedules (kind enum fixed_times/interval/as_needed), intakes (dose logs), and the empty-seam drug_safety_facts (reserved, stays empty — no inference) + endpoints returning computed dose state + engine unit tests + integration test. | M0.1–M0.4 (foundation backend: kernel, auth, plugin framework, migration/error harness); M1 backend (reference engine to copy). | Backend-engine: plugin registered on the kernel; migration expand-contract and every column NOT NULL with a default; endpoints return server-computed dose state; engine unit tests encode the 60-minute constant literally and pass; integration test vs. real Postgres passes (idempotency replay; duplicate-dose-log rejected; unattachable-log fail-safe); the 60min grace constant appears nowhere outside the engine core/ package. | 03 §5; 04 §1,§4,§5,§8,§9; 13 §3,§5 |
S-<date>-M5-web-NN | web | Medication feature on the React+TanStack dashboard: medication list + schedule CRUD and log-within-grace at route /engines/medication; query key ['engine','medication',date] invalidated by the log-medication mutation; renders per-dose grace state (open / logged / missed / late) verbatim from the server. | S-<date>-M5-backend-*; M0.5–M0.7 (web shell, theme engine, i18n engine); M1 web (reference card). | Client-engine: renders engine-endpoint state and submits log intents only; thin-client/SSOT audit passes (no 60-min window / grace / overdue logic in client — grep finds no protocol constant); both themes render per the strict color spec; Arabic RTL mirrors (incl. any progress/countdown bars); strings are ICU, none concatenated. | 05 §3 (routes/query keys/endpoints); 03 §5; 13 §5 |
S-<date>-M5-mcp-NN | mcp | log_medication MCP tool (+ CLI equivalent): logs a medication within the 60-minute grace window by calling the real backend endpoint with the personal-access-token; sends Idempotency-Key; returns server dose state verbatim. | S-<date>-M5-backend-*; M0.8 (MCP server + CLI skeleton). | MCP: the tool calls the real API with PAT auth, returns server state verbatim, holds no protocol logic and no AI keys; excluded tools stay excluded (no complete_break, no diagnosis/analysis passthrough). | 11 (tool table, Core Opinions, auth); 03 §5; 13 §5 |
S-<date>-M5-chrome-NN (OPTIONAL) | chrome | Optional MV3 surface: a simple medication reminder/log affordance (grace-window-open indicator on badge/popup + one-tap log intent). Whether meds appears in the extension at all is an open product question — skip unless prioritized. | S-<date>-M5-backend-*; M1 chrome (extension shell, poll worker, auth). | Client-engine (same as web): renders server state, submits log intent only; thin-client/SSOT audit passes; both themes + RTL correct; ICU strings. Reminder/badge visual is PENDING DESIGN. | 06 §3.2,§7,§9 (badge/popup/side-panel, optional-scope note); 03 §5; 13 §5 |
S-<date>-M5-apple-NN | apple | Swift medication screens (iOS + relevant widget/complication surface): med list, schedule management, and log-within-grace, rendering the server's grace-window state. This is the session that closes divergence #1 for meds — screens only, zero client-side protocol logic. | S-<date>-M5-backend-*; M1 apple (suite shell, HDDesignSystem tokens, API client). | Client-engine: renders engine state, submits log intents only (server-confirmed writes only, no optimistic protocol writes); thin-client/SSOT audit passes; both themes per color spec; Arabic RTL mirrors; ICU strings, none concatenated. Screen layouts PENDING DESIGN. | 07 §11 (Divergence #1), §4 (consumption map), §10 (tokens); 02 §2.1; 03 §5; 13 §5 |
S-<date>-M5-android-NN | android | Kotlin medication screens (phone + relevant Glance/Wear/Tile surface): med list, schedule management, log-within-grace; local reminder armed at server-provided instants only (client never computes the window). | S-<date>-M5-backend-*; M1 android (suite shell, tokens, API client). | Client-engine: renders server dose state, submits log intents only; local reminders fire at backend-computed instants (no client window math); thin-client/SSOT audit passes; both themes per color spec (physical-device check); Arabic RTL mirrors; ICU strings. | 08 § (medication rows: notification schedule + medication/log), §channels; 03 §5; 13 §5 |
S-<date>-M5-desktop-NN | desktop | Electron medication surface, reusing the web components: med list + schedule + log-within-grace against the same API. | S-<date>-M5-backend-*; S-<date>-M5-web-* (component reuse); M1 desktop (Electron shell). | Client-engine: renders server state, submits log intents only; thin-client/SSOT audit passes; both themes + RTL correct; ICU strings. | 09 (desktop action map, medication row); 05 §3; 03 §5; 13 §5 |
Session count: 7 (6 required + 1 optional chrome). Backend is the hard prerequisite for all six client sessions.
Notes & gotchas
- Legacy precedents worth reusing. Overdue = advisory only and skipped counts as kept come straight from the legacy behaviour and align with Opinion 2 — the engine reports the logging fact and never nags, penalizes, or advises on a missed/skipped dose. Preserve this as engine behaviour, not client behaviour.
- Hard Opinion-2 liability to avoid. The engine must never map a medication name to a drug class, validate a dosage, or check interactions.
drug_safety_factsis a reserved empty seam (all columns NOT NULL + default, but no rows and no read path in this build) awaiting a licensed provider — do not populate or infer it. Any medication-related uncertainty routes to "Consult your doctor." - Fail-safe on ambiguous logs. If a dose log cannot be matched to a scheduled window, store it unattached and prompt the user; never guess the target dose. Duplicate logs for one window are rejected but kept in the audit trail.
as_needed(PRN) has no grace window. Onlyfixed_timesandintervalschedules generate scheduled doses / overdue advisories; PRN meds are log-only. Keep this branch explicit incore/.- PENDING DESIGN / open decisions:
- Anchor semantics (open question 03 §11.5): whether the 60 minutes run strictly after the scheduled time, are centered (±30), or are user-configurable is undefined. Model abstractly as
[window_open, window_close]; resolve before schema freeze. - Timezone travel mid-schedule — dose anchoring policy is an open question (shared with the day-boundary policy, 03 §0.5 / 04 open questions).
- Reminder/alert presentation across surfaces — PENDING DESIGN (03 §5.4); med screen layouts (web, apple, android) — PENDING DESIGN.
- Chrome surface is optional — whether Medication Grace appears in the extension (badge/popup/timeline) is an unresolved product question (06 §7,§9.2,§12); its reminder indicator is PENDING DESIGN. Skip the chrome session unless explicitly prioritized.
- Anchor semantics (open question 03 §11.5): whether the 60 minutes run strictly after the scheduled time, are centered (±30), or are user-configurable is undefined. Model abstractly as
- Path naming. Platform PRDs sketch differing medication paths; the backend session is the authority that fixes canonical
/api/v1/routes (04 §7), and every client conforms to Phase 1 — do not treat the client sketches as contract.
Handoff reminder
At session end (per 13 §6): write one session-handoff memory to the healthdebug brain — retain the content in the §6 structure, then memory/edit to attach metadata {project:"healthdebug", category:"session-handoff", era:"greenfield", status:"current", surface:"<surface>", module:"M5", title:"Handoff <session-id>", next:"<module+surface>"} with a unique source_ref: handoff/M5-medication-grace/<surface>/<n> (never reused). Then update the single build-ledger memory (source_ref: build-ledger, edited in place) so one recall shows the whole frontier and the recommended next session.