# The 8 Protocol Engines — State Machine Specifications

**Project:** Health Debug (healthdebug.com) — greenfield build
**Document:** 03 — Protocol Engines (planning only, no implementation)
**Status:** Draft for review. UI-dependent items are flagged inline as **PENDING DESIGN**.
**Date:** 2026-09-01

## Purpose

This document specifies the eight Protocol Engines as backend state machines. Each engine is defined by its purpose, states, transitions, invariants, exact parameters from the product spec, inputs/outputs, a proposed REST API surface (endpoint names only), edge cases, and fail-safe behavior. Every engine is constrained by the Two Core Opinions, and every engine runs **only** in the ToGO backend, which is the Single Source of Truth (SSOT) for all temporal and state logic. Clients (React web, native Swift, native Kotlin, Electron, Chrome extension) are presentation and sensor-collection layers: they render engine state returned by the API and push sensor/user events to it. They never compute or hardcode protocol logic locally.

---

## 0. Global Rules That Bind All Engines

### 0.1 The Two Core Opinions (non-negotiable)

1. **The Protocol is Categorical, Not Quantitative.** Engines evaluate inputs by *what* they are, not *how much*. Food is strictly `Safe` or `Trigger-bearing`. There is no quantitative AI deduction logic for food anywhere in the system. Where an engine has a numeric parameter (e.g., 250ml hydration units, 90-minute caffeine block), the number is a **fixed protocol constant**, not an input to a quantitative model.
2. **The App NEVER Invents Medical Certainty.** No engine, and no AI feature layered on top of engine data, may diagnose, prescribe, or guess drug categories. AI (BYOK, proxied server-side) does behavioral pattern analysis only. Every engine output that could be read as medical advice must terminate in: **"Consult your doctor."**

### 0.2 SSOT and client contract

- All timers, windows, cooldowns, counters, and state transitions are computed on the ToGO backend. Time comparisons happen server-side.
- Clients send **events** (a log entry, a sensor sample, a tap) and receive **engine state snapshots** (current state, remaining time, counters, violation flags) to render.
- A client showing a countdown may animate locally for smoothness, but the authoritative value is always the server snapshot; on reconnect/refresh, the client discards local extrapolation. **PENDING DESIGN:** exact refresh/animation cadence per surface (web, watch, extension badge, widgets, CarPlay/Android Auto).
- Push/alert delivery rules per device class are **PENDING DESIGN**.

### 0.3 Persistence conventions (from the tech-stack rules)

- PostgreSQL via sqlc + Atlas. **Every column NOT NULL with a defined default.** Nullable-looking concepts (e.g., "no prediction yet") are modeled as explicit enum states or sentinel defaults, never SQL NULL.
- All schema changes follow the **expand-contract** migration pattern.
- Engine state is derivable: each engine persists an **event log** (append-only) plus a **materialized current-state row** per user for fast reads. The event log is the source for recomputation and for AI behavioral pattern analysis.
- **Each engine is its own ToGO plugin** (decided 2026-09-01): registered on the Microkernel plugin registry with its own routes, config, and hook subscriptions, so engines are independently manageable and implementable (plugin layout in `04-backend-togo.md` §8). Hooks carry cross-engine concerns (intake fan-out, future doctor-restriction overlays — see §10).

### 0.4 Common state-machine vocabulary

Unless otherwise noted, each engine exposes:

- `state` — the engine's current named state (enum).
- `since` — server timestamp the state was entered.
- `next_transition_at` — server timestamp of the next scheduled automatic transition (or a sentinel when none).
- `violations` — categorical violation records (what rule, when), never quantities-based judgments.

### 0.5 Undefined-by-spec global items (do not invent)

- **Day boundary / reset time:** several engines accumulate "per day" (Hydration cap) or anchor to daily events (wake-up, sleep). The spec does not define the reset boundary, timezone handling, or DST behavior. **Open question — must be decided before Phase 1 schema freeze.**
- **Violation UX:** whether a categorically disallowed log is *rejected* by the API or *accepted and flagged as a violation* is a product decision the spec does not settle. This document assumes **accept-and-flag** (honest journaling beats forced compliance) but marks it as an open question. The alert rendering of a violation is **PENDING DESIGN**.

---

## 1. Hydration Engine

### 1.1 Purpose

Track water intake in fixed categorical units and pace intake with a cooldown, so the user builds a steady hydration habit relevant to kidney health and gout prevention.

### 1.2 Exact parameters (from spec)

| Parameter | Value | Notes |
|---|---|---|
| Unit size | **250 ml** | The only loggable increment. Categorical: a log is "one unit of water," not a free-form quantity. |
| Maximum total | **5000 ml** | Hard cap (20 units). Spec does not state the accumulation window; assumed daily — see open question §0.5. |
| Entry cooldown | **30 seconds** | Minimum gap between two accepted entries. |

### 1.3 States and transitions

```mermaid
stateDiagram-v2
    [*] --> Idle
    Idle --> Cooldown : unit logged (+250ml)
    Cooldown --> Idle : 30s elapsed (server timer)
    Cooldown --> Cooldown : entry attempted → rejected (COOLDOWN_ACTIVE)
    Idle --> Capped : total reaches 5000ml
    Cooldown --> Capped : 30s elapsed AND total = 5000ml
    Capped --> Capped : entry attempted → rejected (CAP_REACHED)
    Capped --> Idle : accumulation window resets
```

- `Idle` — accepting entries; total < 5000 ml.
- `Cooldown` — last accepted entry was < 30 s ago; new entries rejected with a machine-readable reason.
- `Capped` — total = 5000 ml; no further entries until the window resets.

### 1.4 Invariants

- Total is always a multiple of 250 and `0 ≤ total ≤ 5000`.
- No two accepted entries for the same user are less than 30 s apart (server clock; enforced transactionally).
- Cooldown and cap checks run **only** server-side. Clients may gray out the button from the snapshot, but the API is the enforcer. **PENDING DESIGN:** disabled-button/cooldown countdown visual (neon `#00FF33` glow behavior in dark mode).

### 1.5 Inputs / outputs

- **Inputs:** "log one unit" event (client tap from any surface: web, watch complication, extension, widget, CarPlay/Android Auto voice-safe control — surfaces **PENDING DESIGN**). No free-text quantity input exists.
- **Outputs:** snapshot `{ state, total_ml, units_logged, cooldown_remaining, cap_remaining }`.

### 1.6 REST surface (names only)

| Endpoint | Purpose |
|---|---|
| `POST /v1/engines/hydration/entries` | Log one 250 ml unit |
| `GET  /v1/engines/hydration/state` | Current snapshot |
| `GET  /v1/engines/hydration/entries` | Entry history |
| `DELETE /v1/engines/hydration/entries/{id}` | Undo a mistaken entry (window rules — open question) |

### 1.7 SQL column sketch (planning artifact)

```
hydration_entries
  id           uuid        NOT NULL DEFAULT gen_random_uuid()
  user_id      uuid        NOT NULL DEFAULT '00000000-...'   -- FK, real value always set by API
  amount_ml    integer     NOT NULL DEFAULT 250              -- always 250; column exists for auditability
  logged_at    timestamptz NOT NULL DEFAULT now()
  source       text        NOT NULL DEFAULT 'unknown'        -- enum: web|ios|watchos|android|wearos|extension|desktop
```

### 1.8 Edge cases and fail-safe

- **Double-tap / retry storms:** cooldown absorbs client retries; idempotency keys on `POST` recommended.
- **Offline watch entries synced late:** entries carry client capture time, but cooldown/cap are validated against the **server-side ordering on ingest**; conflicting late entries are rejected or flagged, never silently merged. Exact late-sync policy — open question.
- **Fail-safe:** on any ambiguity (clock skew, duplicate suspicion) the engine **declines to count** the entry rather than inflating totals.

### 1.9 Two Core Opinions applied

- Categorical: intake is counted in identical fixed units — "a unit of water was consumed," never "how much fluid was that really?" No ml free-entry, no beverage-volume inference.
- No medical certainty: the engine never says "you are dehydrated" or ties intake to a diagnosis. Any health-facing narrative from AI over hydration history is pattern description only, ending with "Consult your doctor."

---

## 2. Caffeine Block Engine

### 2.1 Purpose

Enforce the protocol rule that no caffeine is consumed during the first 90 minutes after waking (cortisol-window habit for desk professionals).

### 2.2 Exact parameters (from spec)

| Parameter | Value |
|---|---|
| Block duration | **90 minutes** starting at wake-up |
| Blocked category | Caffeine-bearing items (categorical membership, not mg) |

The **wake-up event source** (manual "I'm awake" log vs. wearable sleep-end via HealthKit/Health Connect vs. both with precedence) is not defined in the spec — open question.

### 2.3 States and transitions

```mermaid
stateDiagram-v2
    [*] --> AwaitingWake
    AwaitingWake --> Blocked : wake-up event received
    Blocked --> Clear : 90 minutes elapsed (server timer)
    Blocked --> Blocked : caffeine log → violation recorded (accept-and-flag, see §0.5)
    Clear --> AwaitingWake : next sleep/day cycle begins (boundary — open question)
```

- `AwaitingWake` — no wake event yet for the current cycle; caffeine logs are un-evaluable (see fail-safe).
- `Blocked` — inside the 90-minute window; caffeine-bearing logs are categorical violations.
- `Clear` — window elapsed; caffeine logs are protocol-compliant (subject to other engines, e.g., GERD Window).

### 2.4 Invariants

- The 90-minute countdown is computed only from the server-recorded wake event; clients never derive "blocked/clear" locally.
- Whether an item "is caffeine" is a lookup against the categorical item catalog (shared with Engine 4) — a boolean membership, never a milligram estimate.

### 2.5 Inputs / outputs

- **Inputs:** wake-up event; beverage/food log events (item id from catalog).
- **Outputs:** `{ state, block_remaining, wake_time, violations_today }`. **PENDING DESIGN:** countdown presentation (dark-mode `#00FF33` glowing timer per color spec).

### 2.6 REST surface (names only)

| Endpoint | Purpose |
|---|---|
| `POST /v1/engines/caffeine/wake` | Record wake-up event |
| `GET  /v1/engines/caffeine/state` | Current snapshot |
| `GET  /v1/engines/caffeine/violations` | Violation history |

(Caffeine consumption itself is logged through the shared intake-log endpoint of Engine 4; this engine evaluates those events.)

### 2.7 Edge cases and fail-safe

- **No wake event logged:** the engine cannot place the window. **Fail-safe: it does not guess a wake time.** Caffeine logs made in `AwaitingWake` are stored and marked `unevaluated`, and the user is prompted to log wake-up. No retroactive violation is invented.
- **Multiple wake events (naps, corrections):** precedence rule (first vs. latest vs. manual-overrides-sensor) is undefined — open question.
- **Wearable clock skew / late sync:** server ingest time vs. device sample time reconciliation policy — open question (shared with §1.8).

### 2.8 Two Core Opinions applied

- Categorical: an espresso and a cola are equally "caffeine-bearing." The engine never computes caffeine mg, half-life curves, or tolerance models.
- No medical certainty: violations are stated as protocol facts ("caffeine during block window"), never as physiological claims. AI commentary on caffeine patterns defers to "Consult your doctor."

---

## 3. GERD Window Engine

### 3.1 Purpose

Protect the pre-sleep digestive window for GERD-prone users: during the 4 hours before sleep, only an explicit whitelist of intake is protocol-compliant.

### 3.2 Exact parameters (from spec)

| Parameter | Value |
|---|---|
| Window length | **4 hours** before sleep |
| Whitelist (ONLY items allowed) | **water, chamomile, anise** |

The **sleep-time anchor** (user-configured bedtime, wearable-predicted sleep onset, or user "going to sleep now" event) is not defined in the spec — open question. The window is *pre*-sleep, so anchoring on a planned/target bedtime is implied, but the source of that target is undefined.

### 3.3 States and transitions

```mermaid
stateDiagram-v2
    [*] --> Open
    Open --> WindowActive : now >= (sleep anchor - 4h)
    WindowActive --> WindowActive : whitelist item logged → compliant
    WindowActive --> WindowActive : non-whitelist item logged → violation recorded
    WindowActive --> Sleeping : sleep event received
    Sleeping --> Open : wake event (hand-off to Caffeine Block engine)
```

- `Open` — daytime; GERD window not active; all catalog evaluation deferred to Engine 4 only.
- `WindowActive` — inside the 4-hour pre-sleep window; the whitelist is the **entire** allowed set.
- `Sleeping` — between sleep and wake events; intake logs during this state are stored and flagged for review (spec does not define this case — see §3.7).

### 3.4 Invariants

- The whitelist is closed: exactly `{water, chamomile, anise}`. There is no "small amounts are fine" carve-out — quantity is irrelevant by Opinion 1.
- Window activation time is computed server-side from the sleep anchor; clients only render `window_starts_at` / `window_active`.

### 3.5 Inputs / outputs

- **Inputs:** sleep-anchor configuration/events; intake log events (item id).
- **Outputs:** `{ state, window_starts_at, window_remaining, allowed_items: [water, chamomile, anise], violations }`. **PENDING DESIGN:** how the window countdown and the whitelist chip UI appear; alert color `#FF3333` per color spec.

### 3.6 REST surface (names only)

| Endpoint | Purpose |
|---|---|
| `GET  /v1/engines/gerd/state` | Current snapshot incl. window boundaries |
| `PUT  /v1/engines/gerd/sleep-anchor` | Set/update the sleep-time anchor |
| `POST /v1/engines/gerd/sleep` | Record actual sleep event |
| `GET  /v1/engines/gerd/violations` | Violation history |

### 3.7 Edge cases and fail-safe

- **No sleep anchor set:** fail-safe — the engine does not guess a bedtime and reports `state: unanchored`, prompting configuration. No violations are generated while unanchored.
- **Bedtime moved after window started** (user postpones sleep): recomputation policy for already-flagged logs is undefined — open question.
- **Shift workers / irregular sleep:** supported only to the extent the anchor is updated; the engine never infers a schedule.
- **Logs during `Sleeping`:** undefined by spec; proposed handling is store-and-flag-for-review, not auto-violation.

### 3.8 Two Core Opinions applied

- Categorical in its purest form: the window check is set membership against a 3-item whitelist. "A sip of juice" and "a full meal" are the same violation class.
- No medical certainty: the engine never claims reflux occurred or will occur; it reports window compliance and defers symptom interpretation to "Consult your doctor."

---

## 4. Trigger Families Engine

### 4.1 Purpose

Provide the categorical food/intake catalog underpinning the whole protocol: every catalog item is `Safe` or `Trigger-bearing`, and trigger-bearing items belong to trigger families.

### 4.2 Exact parameters (from spec)

| Parameter | Value |
|---|---|
| Item classification | Strictly binary: **Safe** / **Trigger-bearing** |
| Trigger families | **Gout**, **IBS-GERD**, **Fatty Liver** |

Who curates/seeds the catalog (admin team, clinical advisors, future doctor dashboard) is not defined — open question. Whether an item can belong to multiple families simultaneously is not stated; the categorical model naturally allows multi-family membership and this document assumes it — open question to confirm.

### 4.3 States and transitions

This engine is less a timer and more a **classification service + per-user exposure ledger**. Item lifecycle:

```mermaid
stateDiagram-v2
    [*] --> Unclassified : item proposed (e.g., user free-text)
    Unclassified --> Safe : curated as Safe
    Unclassified --> TriggerBearing : curated into >=1 family
    Safe --> TriggerBearing : reclassification (expand-contract on catalog)
    TriggerBearing --> Safe : reclassification
```

Per-user, each intake log is evaluated at write time into an exposure record: `{item, families[], evaluated_as: safe|trigger, engines_notified[]}`.

### 4.4 Invariants

- No item is ever "70% risky" or "risky above X grams." Classification is binary per family membership.
- `Unclassified` items produce **no protocol judgment** (fail-safe: unknown ≠ safe, unknown ≠ trigger; it is explicitly "unevaluated — consult your doctor if unsure").
- Other engines (Caffeine Block, GERD Window) consume this catalog for their own categorical checks (caffeine-bearing flag, GERD whitelist membership).

### 4.5 Inputs / outputs

- **Inputs:** intake log events (item id or proposal of a new item); catalog curation operations (admin scope).
- **Outputs:** item classification lookups; per-user exposure timeline grouped by family (the raw material for AI behavioral pattern analysis).

### 4.6 REST surface (names only)

| Endpoint | Purpose |
|---|---|
| `GET  /v1/catalog/items` | Browse/search categorical catalog |
| `GET  /v1/catalog/items/{id}` | Item + family memberships |
| `POST /v1/catalog/items/proposals` | Propose an unclassified item |
| `POST /v1/intake/logs` | Log an intake event (shared entry point evaluated by Engines 2, 3, 4) |
| `GET  /v1/engines/triggers/exposures` | Per-user exposure timeline by family |

### 4.7 SQL column sketch (planning artifact)

```
catalog_items
  id            uuid    NOT NULL DEFAULT gen_random_uuid()
  name_key      text    NOT NULL DEFAULT ''        -- ICU message key; no string concatenation (i18n rule)
  classification text   NOT NULL DEFAULT 'unclassified'  -- enum: unclassified|safe|trigger_bearing
  family_gout        boolean NOT NULL DEFAULT false
  family_ibs_gerd    boolean NOT NULL DEFAULT false
  family_fatty_liver boolean NOT NULL DEFAULT false
  caffeine_bearing   boolean NOT NULL DEFAULT false   -- consumed by Engine 2
  gerd_whitelisted   boolean NOT NULL DEFAULT false   -- exactly water/chamomile/anise
```

### 4.8 Edge cases and fail-safe

- **Free-text food the catalog doesn't know:** stored as `Unclassified`, no judgment rendered, user told the item is unevaluated. The AI is **prohibited** from guessing its classification (Opinion 2 by extension of "no invented certainty"); curation is human.
- **Reclassification of an item:** historical exposure records keep the classification **as evaluated at log time** (audit honesty); recomputation policy — open question.
- **Localization:** item names via ICU Message Format keys, full RTL Arabic support; never concatenate family labels into sentences in code.

### 4.9 Two Core Opinions applied

- This engine **is** Opinion 1 institutionalized: the binary Safe/Trigger model with three named families is the only food logic in the system. No portion sizes, no glycemic math, no purine milligrams.
- Opinion 2: family membership is presented as protocol categorization, never as "this food caused your flare." Correlation narratives from AI are behavioral observations ending in "Consult your doctor."

---

## 5. Medication Grace Engine

### 5.1 Purpose

Give users a humane, flexible window to log scheduled medications, tracking adherence-to-logging without ever reasoning about the drugs themselves.

### 5.2 Exact parameters (from spec)

| Parameter | Value |
|---|---|
| Grace window | **60 minutes**, flexible, for logging a medication |

The **anchor semantics** are undefined by spec: whether the 60 minutes run strictly after the scheduled time, or ±30 around it, or user-configurable — open question. This document models it abstractly as `[window_open, window_close]` spanning 60 minutes around/after the scheduled dose time.

### 5.3 States and transitions (per scheduled dose)

```mermaid
stateDiagram-v2
    [*] --> Scheduled
    Scheduled --> GraceOpen : window_open reached
    GraceOpen --> Logged : user logs the dose
    GraceOpen --> Missed : window_close reached without log
    Missed --> LateLogged : user logs after close (flagged late, never hidden)
    Logged --> [*]
    LateLogged --> [*]
```

### 5.4 Invariants

- The engine stores **user-entered medication names/schedules verbatim**. It never maps a name to a drug class, never validates dosage, never checks interactions (hard Opinion 2 prohibition).
- `Missed` is a logging fact, not a health judgment. The engine **never** advises taking a missed dose, doubling up, or skipping — any such question is answered only with "Consult your doctor."
- Window boundaries computed server-side; reminders fired from server schedule. **PENDING DESIGN:** reminder/alert presentation across surfaces.

### 5.5 Inputs / outputs

- **Inputs:** medication schedule CRUD (name, times — free text/user-defined); "dose taken" log events.
- **Outputs:** per-dose state, upcoming windows, adherence-to-logging history (for AI behavioral patterning only).

### 5.6 REST surface (names only)

| Endpoint | Purpose |
|---|---|
| `POST /v1/medications` | Create a medication schedule |
| `GET  /v1/medications` | List schedules |
| `PUT  /v1/medications/{id}` | Update schedule |
| `POST /v1/engines/medgrace/doses/{id}/log` | Log a dose within/after grace |
| `GET  /v1/engines/medgrace/state` | Today's dose windows and states |

### 5.7 Edge cases and fail-safe

- **Timezone travel mid-schedule:** dose anchoring policy — open question (shared with §0.5).
- **Duplicate log for the same dose window:** second log rejected as duplicate, kept in audit trail.
- **Fail-safe:** when the engine cannot determine which scheduled dose a log belongs to, it stores the log unattached and asks the user, rather than guessing.

### 5.8 Two Core Opinions applied

- Categorical: a dose is `Logged`/`Missed`/`LateLogged` — never "80% adherent by mg."
- Opinion 2 is at its strictest here: no drug-category guessing (explicitly prohibited by spec), no prescribing, no interaction warnings. The engine is a logging clock, and every medication-related uncertainty routes to "Consult your doctor."

---

## 6. Cycle Engine (Fertility)

### 6.1 Purpose

Track menstrual cycles and offer predictions **only** once enough consistent user-logged data exists, failing safe to "no prediction" whenever the data is irregular.

### 6.2 Exact parameters (from spec)

| Parameter | Value |
|---|---|
| Calibration requirement | **3 consecutive logged cycles** before any prediction is made |
| Irregularity rule | On irregularity, the engine **fails safe: no prediction** |

The quantitative definition of "irregularity" (e.g., variance threshold in days) is **not defined in the spec** — open question. Until defined, the planning assumption is that any break in consecutive logging resets calibration, and any detected irregularity suspends predictions entirely.

### 6.3 States and transitions

```mermaid
stateDiagram-v2
    [*] --> Insufficient
    Insufficient --> Insufficient : cycle logged (count < 3 consecutive)
    Insufficient --> Calibrated : 3rd consecutive cycle logged
    Calibrated --> Calibrated : regular cycle logged (prediction refreshed)
    Calibrated --> Suspended : irregularity detected → predictions withdrawn
    Insufficient --> Insufficient : gap in logging → consecutive counter resets
    Suspended --> Insufficient : recalibration restarts (3 consecutive cycles required again)
```

- `Insufficient` — fewer than 3 consecutive logged cycles. **Output: no prediction, ever.** The UI shows calibration progress only (**PENDING DESIGN**).
- `Calibrated` — prediction available and clearly labeled as an estimate.
- `Suspended` — irregularity detected; predictions are withdrawn, not "widened." Whether recalibration requires a full fresh 3-cycle run is implied by fail-safe posture but not explicit in spec — open question.

### 6.4 Invariants

- **No prediction leaves the API in `Insufficient` or `Suspended` states.** The prediction field carries an explicit `unavailable` sentinel (NOT NULL rule: sentinel, not SQL NULL), plus a reason code.
- "Consecutive" means no unlogged cycle between logged ones; the engine never infers a skipped cycle happened.
- Predictions are never framed as fertility guarantees or contraceptive advice (Opinion 2); accompanying copy defers to "Consult your doctor."

### 6.5 Inputs / outputs

- **Inputs:** cycle start/end log events (user-entered).
- **Outputs:** `{ state, consecutive_cycles_logged, prediction: value|unavailable, reason }`.

### 6.6 REST surface (names only)

| Endpoint | Purpose |
|---|---|
| `POST /v1/engines/cycle/logs` | Log a cycle event |
| `GET  /v1/engines/cycle/state` | Calibration state + prediction-or-unavailable |
| `GET  /v1/engines/cycle/history` | Logged cycle history |

### 6.7 Edge cases and fail-safe

- **Backfilled cycles:** whether historical backfill counts toward "consecutive" — open question. Fail-safe default: backfill is stored but calibration counts only forward-logged cycles unless product decides otherwise.
- **Contradictory edits** (user edits a past cycle making history irregular): engine drops to `Suspended` immediately — it prefers withdrawing a prediction over defending one.
- **Fail-safe is the headline rule:** ambiguous data → **no prediction**. The engine never smooths, averages away, or "best-guesses" an irregular history.

### 6.8 Two Core Opinions applied

- Categorical: state is `Insufficient` / `Calibrated` / `Suspended` — the user never sees a confidence percentage.
- Opinion 2: predictions are behavioral-data extrapolations explicitly labeled non-medical; anything touching fertility decisions, symptoms, or contraception efficacy ends in "Consult your doctor."

---

## 7. Contraceptive Security Engine

### 7.1 Purpose

Track contraceptive method adherence with per-method state machines, because a daily pill, a monthly injection, and a long-term implant have fundamentally different schedules and failure modes.

### 7.2 Exact parameters (from spec)

| Parameter | Value |
|---|---|
| Differentiated method types | **Daily pills**, **monthly injections**, **implants** |

The spec defines only that the engine differentiates these three types. Per-type details (pill-time grace rules, injection due-date windows, implant validity duration/expiry reminders) are **not defined** — open questions. The sketches below model only the structural differentiation.

### 7.3 States and transitions (per active method)

```mermaid
stateDiagram-v2
    [*] --> MethodSelected
    MethodSelected --> DailyTrack : type = daily pill
    MethodSelected --> MonthlyTrack : type = monthly injection
    MethodSelected --> LongTermTrack : type = implant

    state DailyTrack {
        [*] --> DoseDue
        DoseDue --> DoseLogged : pill logged
        DoseDue --> DoseMissed : day window passes (window rules — open question)
        DoseLogged --> DoseDue : next day
        DoseMissed --> DoseDue : next day (missed recorded, never advised upon)
    }
    state MonthlyTrack {
        [*] --> InjectionCurrent
        InjectionCurrent --> InjectionDue : monthly due date reached
        InjectionDue --> InjectionCurrent : injection logged
        InjectionDue --> InjectionOverdue : due date passes unlogged
        InjectionOverdue --> InjectionCurrent : injection logged (late, flagged)
    }
    state LongTermTrack {
        [*] --> ImplantActive
        ImplantActive --> ImplantExpiring : approaching end of validity (duration — open question)
        ImplantExpiring --> ImplantActive : replacement logged
    }
```

### 7.4 Invariants

- Method type is a closed enum: `daily_pill | monthly_injection | implant`. The engine's logic branches on **what** the method is (categorical), never on hormone dosage or brand pharmacology.
- On any missed/overdue state the engine reports the logging fact and **never** states protection status ("you are/aren't protected") — that is invented medical certainty. The only guidance emitted is "Consult your doctor."
- Reminder scheduling is server-side. **PENDING DESIGN:** reminder surfaces and privacy presentation (lock-screen discretion, watch complications).

### 7.5 Inputs / outputs

- **Inputs:** method selection/CRUD; dose/injection/replacement log events.
- **Outputs:** per-method state snapshot, upcoming due items, missed/late history.

### 7.6 REST surface (names only)

| Endpoint | Purpose |
|---|---|
| `POST /v1/engines/contraceptive/methods` | Register a method (typed) |
| `GET  /v1/engines/contraceptive/state` | Current per-method snapshot |
| `POST /v1/engines/contraceptive/events` | Log dose / injection / replacement |
| `GET  /v1/engines/contraceptive/history` | Adherence-to-logging history |

### 7.7 Edge cases and fail-safe

- **Method switching** (pill → injection): old method archived, new state machine instantiated; overlap semantics — open question.
- **Interaction with Cycle Engine:** whether an active contraceptive method should suspend cycle predictions is not in the spec — open question; fail-safe posture suggests suspension, but this must be a product/clinical decision, not an engine guess.
- **Fail-safe:** when due-date math is ambiguous (e.g., missing injection date), the engine reports `unconfigured/ambiguous` and asks the user; it never assumes protection continuity.

### 7.8 Two Core Opinions applied

- Categorical: three method types, each with a small closed state set; no efficacy percentages, no hormone-level modeling.
- Opinion 2: the engine tracks logging events only. All protection, side-effect, and missed-dose consequences route to "Consult your doctor."

---

## 8. Kinetic Pomodoro Engine

### 8.1 Purpose

A desk-work timer for the core audience (programmers/engineers) whose defining feature is that a **break only registers if wearables detect physical movement away from the screen** — sitting through the break with the timer running does not count.

### 8.2 Exact parameters (from spec)

| Parameter | Value |
|---|---|
| Break validation | Break registers **only** if wearable motion data shows physical movement away from the screen |

**Not defined in spec (do not invent):** work-interval length, break length, the quantitative motion threshold (steps/distance/duration), and long-break cycles. Classic 25/5 Pomodoro numbers are *not* in the spec and must not be assumed — open questions for product + design.

### 8.3 States and transitions

```mermaid
stateDiagram-v2
    [*] --> Idle
    Idle --> Working : session started
    Working --> BreakPending : work interval elapsed (length — open question)
    BreakPending --> BreakVerifying : motion samples arriving from watch client
    BreakVerifying --> BreakRegistered : movement-away-from-screen criteria met
    BreakVerifying --> BreakNotRegistered : break time elapses without qualifying movement
    BreakPending --> BreakNotRegistered : no wearable data at all (fail-safe: unverified)
    BreakRegistered --> Working : next work interval starts
    BreakNotRegistered --> Working : next work interval starts (break marked unverified)
    Working --> Idle : session ended
```

### 8.4 Invariants

- **The verification decision is made only on the backend**, from motion data that watchOS/WearOS clients read on-device (HealthKit / Health Connect) and push securely to the ToGO API. The watch never decides "break valid" locally — it is a sensor-collection layer.
- A break with no motion evidence is `BreakNotRegistered` — absence of data is **never** treated as movement (fail-safe direction: don't credit).
- The desk-side clients (web dashboard, Electron, Chrome extension) render timer state from server snapshots; the extension's role at the "screen" side (e.g., detecting continued browsing during break) is **not specified** — open question, and any such signal would still be evaluated server-side.
- **PENDING DESIGN:** timer visuals (dark mode `#1A1A1A` background, `#00FF33` neon glow for the active timer, `#00CCFF` active buttons; light mode `#20A060` / `#0070A0` flat), plus how an unregistered break is communicated without shaming.

### 8.5 Inputs / outputs

- **Inputs:** session start/stop; server-scheduled interval elapses; motion data batches pushed from watch clients.
- **Outputs:** `{ state, interval_remaining, break_status: pending|verifying|registered|not_registered, todays_registered_breaks }`.

### 8.6 REST surface (names only)

| Endpoint | Purpose |
|---|---|
| `POST /v1/engines/pomodoro/sessions` | Start a session |
| `POST /v1/engines/pomodoro/sessions/{id}/stop` | End a session |
| `GET  /v1/engines/pomodoro/state` | Current timer/break snapshot |
| `POST /v1/sensors/motion/batches` | Wearable motion data ingest (shared sensor ingest, consumed by this engine) |
| `GET  /v1/engines/pomodoro/history` | Session and break-verification history |

### 8.7 Edge cases and fail-safe

- **User has no wearable:** every break is `BreakNotRegistered/unverified`. Whether a degraded no-wearable mode exists (e.g., honor-system breaks marked as such) is a product decision — open question; the spec's stance implies breaks simply do not register without wearable evidence.
- **Watch offline during break, syncs later:** retroactive upgrade of `BreakNotRegistered → BreakRegistered` on late evidence — allowed in principle (evidence is evidence); exact re-evaluation window — open question.
- **Motion during break that is not "away from screen"** (e.g., fidgeting at desk): the criteria distinguishing qualifying movement are undefined — open question; fail-safe default is to not credit borderline data.
- **Fail-safe:** all ambiguity resolves to "break not registered." The engine under-credits rather than over-credits.

### 8.8 Two Core Opinions applied

- Categorical: a break is `Registered` or `Not Registered` — binary, no "70% of a break" or movement-quality scoring surfaced to the user.
- Opinion 2: the engine makes no claims about musculoskeletal or cardiovascular benefit; AI summaries of sitting/break patterns are behavioral observations ending in "Consult your doctor."

---

## 9. Cross-Engine Architecture Notes

### 9.1 Shared intake pipeline

Engines 2 (Caffeine), 3 (GERD), and 4 (Trigger Families) all evaluate the **same** intake log event through the categorical catalog. One `POST /v1/intake/logs` fan-outs via ToGO Microkernel hooks to each engine's evaluator; each engine records its own verdict independently. A single cola at 10 p.m. can simultaneously be: caffeine-compliant (block elapsed), a GERD-window violation (not whitelisted), and a trigger-family exposure — three categorical facts, no arithmetic.

### 9.2 Proposed backend module layout (planning sketch)

```
services/protocol/
  engines/
    hydration/        # states, cooldown/cap evaluators
    caffeine/         # wake anchor, 90-min block
    gerd/             # sleep anchor, 4h window, whitelist
    triggers/         # catalog + exposure ledger
    medgrace/         # dose windows (60-min grace)
    cycle/            # calibration counter, fail-safe predictor gate
    contraceptive/    # typed method machines
    pomodoro/         # intervals + motion verification
  catalog/            # shared categorical item catalog
  sensors/            # HealthKit / Health Connect ingest normalization
  snapshots/          # per-user materialized engine states
```

(Exact ToGO project conventions to be confirmed against the ToGO Framework scaffolding in Phase 1; the old togo-based repo at github.com/fadymondy/health-debug is reference-only and nothing is inherited from it.)

### 9.3 Sensor ingest boundary

- Apple HealthKit and Google Health Connect are read **on-device** by the native clients; normalized samples are pushed to `POST /v1/sensors/...` endpoints. The backend never pulls from Apple/Google directly.
- Engines consume normalized sensor events (wake/sleep candidates, motion batches) but the **decision** to transition state is always the engine's, server-side.

### 9.4 AI layer relationship

- AI (BYOK: OpenAI, Anthropic, Google, Apple Intelligence; keys encrypted server-side; all calls proxied by the backend; keys never reach clients) reads **engine event logs and snapshots** for behavioral pattern analysis only.
- AI may never: reclassify catalog items, alter engine state, generate predictions the Cycle Engine has withheld, name drug categories, or produce diagnostic/prescriptive text. Every AI output surface carries the "Consult your doctor" terminal deferral.

### 9.5 i18n and rendering constraints on engine outputs

- All engine-facing strings are ICU Message Format keys; no concatenation. Counts/durations use ICU plural/select so Arabic plural rules work.
- Progress and countdown renderings must mirror correctly in RTL (including progress bars) — **PENDING DESIGN** for each surface.

---

## 10. Future-State Hooks (context only — NOT current scope)

- **Doctor "Medical Restrictions":** the future doctor dashboard will prescribe restrictions (blocking foods, enforcing fasting, requiring tracked workouts) that **dynamically alter** engine behavior (e.g., extending the GERD whitelist restriction set, adding blocked catalog items per patient). Engines should therefore read their rule sets through the Microkernel config/hook layer rather than hardcoding them, so a restriction overlay can be applied later without engine rewrites. Design of that overlay is out of scope here.
- **Gamification:** badges for protocol compliance (e.g., perfect GERD window) will consume engine violation/compliance streams; engines only need to keep those streams queryable.
- **B2B / marketplace APIs:** aggregate, consented views over engine data for pharmacies, insurers, wellness clinics — out of scope; noted so engine event logs are designed as clean, exportable streams.

---

## 11. Consolidated Open Questions

1. **Day/window reset boundary and timezone/DST policy** for Hydration cap, wake/sleep cycles, and dose schedules.
2. **Violation handling model:** reject at API vs. accept-and-flag (this doc assumes accept-and-flag).
3. **Wake-up event source and precedence** (manual vs. wearable) for the Caffeine Block.
4. **Sleep-time anchor source** for the GERD Window (configured bedtime vs. wearable vs. explicit event) and recompute policy when bedtime shifts.
5. **Medication Grace anchor semantics:** 60 minutes after scheduled time, or centered on it?
6. **Cycle Engine:** quantitative definition of "irregularity"; whether recalibration after `Suspended` requires a fresh 3-cycle run; whether backfilled cycles count.
7. **Contraceptive Security:** per-type window rules (pill day-window, injection due tolerance, implant validity duration); interaction with Cycle Engine predictions.
8. **Kinetic Pomodoro:** work/break interval lengths, qualifying-motion criteria, late-sync re-evaluation window, and no-wearable degraded mode.
9. **Catalog governance:** who curates Safe/Trigger classifications and multi-family membership policy.
10. **Undo/edit windows** for logged entries (hydration undo, cycle edits) and their effect on already-evaluated state.
11. **All alerting/notification UX and every visual treatment flagged PENDING DESIGN** above await the finalized Figma design.
