Páginas · Health Debug
M2 — Caffeine Block · Session Card
Planning artifact. A build session opens this file (plus the brain — see §5 Handoff) to know exactly what its cell is, what must exist first, and when it is done. One session builds one
(module × surface)cell. Surface order is fixed: backend → web → mcp → chrome → apple → android → desktop (doc 13 §3–§4). The backend session MUST land before any client session for this module.
Purpose
The Caffeine Block engine enforces the protocol rule that no caffeine is consumed during the first 90 minutes after waking (the cortisol-window habit for desk professionals). It is its own ToGO plugin: a pure core/ state machine (AwaitingWake → Blocked → Clear) plus plugin.go registration, a schema migration, and endpoints. The backend is the Single Source of Truth for the 90-minute countdown; every client only renders server state (block active/clear, server-provided deadline) and submits log/wake intents. Classification of a logged drink as clean vs. sugar-based is decided server-side against a drink registry (a DB resource carrying is_sugar_based); clients never categorize. Consistent with Core Opinion 1 the engine is categorical — it records a drink by kind, never milligrams — and consistent with Core Opinion 2 a violation is stated as a protocol fact ("caffeine during block window"), never a physiological claim, and any AI commentary defers to "Consult your doctor."
Engine / feature parameters (exact — do not invent)
| Parameter | Value | Source / status |
|---|---|---|
| Block duration | 90 minutes, starting at the wake event (CaffeineBlockMinutes = 90) | Spec-fixed (doc 03 §2.2). |
| Blocked category | Caffeine-bearing items — categorical membership, not mg | Spec-fixed (Core Opinion 1). |
| Wake-up anchor source | OPEN QUESTION. Legacy inferred wake = work_start − 1h (never asked). Manual "I'm awake" log vs. wearable sleep-end (HealthKit / Health Connect) vs. both-with-precedence is undefined. | doc 03 §2.2 / §7 item 3; legacy candidate answer (doc 12). PENDING DESIGN. |
| Drink classification (clean vs. sugar-based) | Decided server-side via drink registry column is_sugar_based. Legacy seed: sugar-based = red bull / monster / energy drink / soda / cola; clean = cold brew / matcha / green tea / espresso / black coffee / americano; unknown kinds treated as clean. | Legacy precedent seed data (doc 12). Registry is a DB resource; classification is not a client concern. |
| Daily caffeine-log cap | Legacy MaxDailyCaffeineLogs = 8 | Legacy precedent, not spec-fixed — PENDING ratification. Do not treat as a protocol constant until ratified. |
| Caffeine-log cooldown | Legacy CaffeineLogCooldown = 1 min | Legacy precedent, not spec-fixed — PENDING ratification. |
| Violation handling | Accept-and-flag (store the log, record the violation), not reject-at-API | doc 03 §0.5 / §2.3; global assumption pending ratification. |
| Fail-safe (no wake logged) | Engine does not guess a wake time; caffeine logs made in AwaitingWake are stored and marked unevaluated; user is prompted to log wake. No retroactive violation invented. | doc 03 §2.7 — hard invariant. |
State machine (doc 03 §2.3): AwaitingWake → (wake event) → Blocked → (90 min elapsed, server timer) → Clear. In Blocked, a caffeine-bearing log is a categorical violation recorded accept-and-flag. Day/cycle reset boundary (Clear → AwaitingWake) is an OPEN QUESTION.
REST surface (names from doc 03 §2.6; note divergence below): POST /v1/engines/caffeine/wake (record wake), GET /v1/engines/caffeine/state (snapshot: { state, block_remaining, wake_time, violations_today }), GET /v1/engines/caffeine/violations (history). Caffeine consumption itself is a log event fanned out through the shared intake pipeline (doc 03 §9.1); the engine evaluates those events via a kernel hook. PENDING DESIGN: exact log endpoint path — doc 03/11 imply the shared intake log, doc 05 uses /api/v1/intake/entries, doc 06 uses POST /caffeine/log. Reconcile in the backend session and document the chosen path in the handoff.
Sessions
Session ID pattern: S-<date>-M2-<surface>-NN (<date> = the build date, NN = per-day sequence; fill in at session start).
| Session ID | Surface | Scope | Entry deps | Done criteria (doc 13 §5) | Plan docs to read |
|---|---|---|---|---|---|
S-<date>-M2-backend-NN | backend | The caffeineblock ToGO plugin: pure core/ state machine (AwaitingWake/Blocked/Clear, 90-min block, accept-and-flag, no-wake fail-safe); plugin.go registration (routes, hook subscription on the shared intake log, injected clock, migration manifest); expand-contract migration for caffeine state + violations + the drink registry resource (is_sugar_based, name_ar, seeded from legacy lists); is_sugar_based classification done server-side; wake/state/violations endpoints returning computed state; engine unit tests encoding every spec constant literally; integration test vs. real Postgres. Wake anchor: implement a pluggable anchor with the legacy work_start − 1h inference as the default, isolated so a future decision swaps it — flag PENDING. | M0.1–M0.4 (backend foundation: kernel, auth, plugin framework, migration harness); M1 Hydration backend done (reference engine pattern to copy). Coordinate with M4 Trigger Families on the shared categorical catalog / caffeine_bearing flag (doc 03 §4, §9.1) — if M4 catalog is not yet built, the drink registry is defined here and reconciled later. | Backend-engine done when: plugin registered on the kernel; migration is expand-contract and every column is NOT NULL with a default; endpoints return server-computed state; engine unit tests encode every spec constant literally and pass; integration test against real Postgres passes (idempotency replay, log-cooldown/cap races); no protocol constant appears outside the engine's core/ package. | 13, 03 §0/§2/§9, 04 §6/§8, 12 (legacy caffeine + drinks registry) |
S-<date>-M2-web-NN | web | Caffeine Block card + detail route (/engines/caffeine): renders server state, animates a display-only countdown toward the server-provided block deadline (#00FF33 neon-green glow in dark), log-clean-caffeine intent (kind pick only), wake-time change action. No local "is caffeine allowed?" logic. | M0.5–M0.7 (web shell, theme engine, i18n engine); M2 backend done; M1 Hydration web done (reference card pattern). | Client-engine done when: surface renders the engine endpoint state and submits log intents only; thin-client/SSOT audit passes (grep for 90/block-minute/cooldown constants and classification logic finds nothing in client code); both themes render per the strict color spec; Arabic RTL mirrors correctly (incl. any progress/countdown bar); strings are ICU, none concatenated. | 13, 03 §2, 05 §1/§timeline+cards, 04 (response shapes) |
S-<date>-M2-mcp-NN | mcp | Two tools wired to the real API with personal-access-token auth: log_caffeine (log a caffeine drink by kind only, never mg; sends Idempotency-Key) and today_status (full computed day incl. caffeine block state, returned verbatim). Holds no protocol logic, no classification, no AI keys. | M0.8 (MCP server + CLI skeleton); M2 backend done. | MCP session done when: each tool calls the real API with PAT auth, returns server state verbatim, holds no protocol logic and no AI keys, and the excluded tools (no complete_break, no diagnosis passthrough) stay excluded. | 13, 11, 03 §2 |
S-<date>-M2-chrome-NN | chrome | Popup clean-caffeine button + caffeine block indicator: badge/popup mirror the server snapshot (GET /protocol/state), 1-click clean-caffeine intent, server rejection during the block rendered verbatim as an alert-state response. No local pre-validation of the block rule. | M2 backend done; M1 chrome (MV3 shell, poller/badge engine, API client) as reference. Block indicator visual is PENDING DESIGN (doc 06). | Client-engine done when: surface renders server snapshot and submits intents only; thin-client/SSOT audit passes (no block/cooldown/classification logic in extension code); both themes per color spec; Arabic RTL mirrors; ICU strings, none concatenated. | 13, 06 §snapshot+logging, 03 §2 |
S-<date>-M2-apple-NN | apple | iOS Caffeine Block card + relevant widget/watch surface: renders server state and block countdown, submits clean-caffeine + wake intents. Wearable sleep-end as a candidate wake anchor source stays PENDING (server decides). No local protocol logic. | M2 backend done; M1 apple as reference (X3 sensors/HealthKit coordination only if a wearable wake anchor is chosen — PENDING). | Client-engine done when: surface renders engine state and submits intents only; thin-client/SSOT audit passes; both themes per color spec; Arabic RTL mirrors; ICU strings, none concatenated. | 13, 07, 03 §2 |
S-<date>-M2-android-NN | android | Phone Caffeine Block card + relevant Wear/Glance surface: renders server state and block countdown, submits clean-caffeine + wake intents. Health Connect sleep-end as a candidate wake anchor stays PENDING (server decides). No local protocol logic. | M2 backend done; M1 android as reference (X3 Health Connect coordination only if a wearable wake anchor is chosen — PENDING). | Client-engine done when: surface renders engine state and submits intents only; thin-client/SSOT audit passes; both themes per color spec; Arabic RTL mirrors; ICU strings, none concatenated. | 13, 08, 03 §2 |
S-<date>-M2-desktop-NN | desktop | Electron Caffeine Block surface, reusing the web card/components; renders server state + block countdown, submits clean-caffeine + wake intents through the shared API client. | M2 backend done; M2 web done (component reuse); M1 desktop as reference. | Client-engine done when: surface renders engine state and submits intents only; thin-client/SSOT audit passes; both themes per color spec; Arabic RTL mirrors; ICU strings, none concatenated. | 13, 09, 05, 03 §2 |
Notes & gotchas
- Reuse M1's shape. Hydration is the reference engine; copy its plugin layout (
core/+plugin.go+ sqlc queries), its/todayintegration, its client card pattern, and its test harness. M2 sessions should be faster because the pattern is set. - Legacy seed data is ready. The legacy
drinkstable (withname_ar) and the clean/sugar-based kind lists (doc 12) are a ready seed for the drink registry — reuse the classification lists and the admin-CRUD precedent rather than reinventing. Keepis_sugar_basedserver-side only. - Wake anchor is the load-bearing open question (PENDING DESIGN). Do not hardcode a single source. Implement the legacy
work_start − 1hinference as an isolated, swappable default so the eventual decision (manual log vs. wearable sleep-end vs. precedence) is a one-place change. Multiple wake events (naps/corrections) precedence and theClear → AwaitingWakeday-reset boundary are also undefined — fail safe, do not guess. - Categorical only (Core Opinion 1). An espresso and a cola are equally "caffeine-bearing." Never compute mg, half-life, or tolerance. Log by kind.
check/classification is boolean membership against the catalog/registry. - No invented certainty (Core Opinion 2). Violations are protocol facts, not physiological claims; AI commentary on caffeine patterns defers to "Consult your doctor."
- PENDING ratification, not spec-fixed:
MaxDailyCaffeineLogs = 8andCaffeineLogCooldown = 1 minare legacy precedents only — do not encode them as protocol constants until ratified, and if used provisionally, keep them insidecore/and flag them in the handoff. - Endpoint-path divergence (PENDING DESIGN): docs 03/05/06/11 name the caffeine log path differently (shared intake log vs.
/api/v1/intake/entriesvs./caffeine/log). Reconcile in the backend session and record the canonical path in the handoff so all client sessions use it. - Shared intake pipeline liability. Caffeine, GERD, and Trigger Families all evaluate the same intake log event via kernel hooks (doc 03 §9.1). Coordinate the categorical catalog /
caffeine_bearingflag with M4 so the drink registry and the food catalog do not fork. - NOT-NULL + expand-contract is enforced by CI. Every new column (caffeine state, violations, drink registry) must be NOT NULL with a defined default, and the migration must be expand-contract — the schema lint gate (M0.1) will fail otherwise.
- MCP stays thin.
log_caffeine/today_statusreturn server state verbatim; no protocol logic, no classification, no AI keys. Excluded tools stay excluded.
Handoff reminder
At session end, follow doc 13 §6: write one session-handoff memory to the healthdebug brain (retain, then memory/edit to attach metadata) with source_ref: handoff/M2-caffeine-block/<surface>/<n> (unique — never reuse) and metadata {project:"healthdebug", category:"session-handoff", era:"greenfield", status:"current", surface:"<surface>", module:"M2", title:"Handoff <session-id>", next:"<module+surface>", ...}, using the exact content structure in doc 13 §6. 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. Any architectural decision or new open question (e.g., the wake-anchor resolution) also gets its own classified memory and, if it changes the plan, an edit to the relevant plan doc.