# M8 — Kinetic Pomodoro — Session Card

> Planning artifact. A future build session opens this to know exactly what its cell is, what must be done first, and when it is done. Read alongside `docs/plan/13-execution-modules-and-sessions.md` (the framework), `docs/plan/03-protocol-engines.md §8` (the engine spec), `docs/plan/04-backend-togo.md` (plugin / migration / test conventions), and the relevant platform PRD (05 web, 06 chrome, 07 apple, 08 android, 09 desktop, 11 mcp).

## 1. Purpose

Kinetic Pomodoro is a desk-work focus timer for the core audience (programmers/engineers) whose defining, non-negotiable rule is that **a break registers only when a wearable detects physical movement away from the screen** — sitting through the break with the timer running does not count, and the absence of motion data is *never* treated as movement (fail-safe: under-credit, never over-credit). As with every engine, the ToGO backend is the Single Source of Truth: the pomodoro plugin owns the work/break state machine and the verification decision; watchOS (CoreMotion) and WearOS clients are pure **sensor-collection layers** that push motion evidence; and every desk-side surface (web, chrome, apple menu-bar/overlay, android, desktop) only renders server state and reports evidence. Because the whole point of the module is wearable-validated breaks, the **watch and Wear motion sub-sessions are mandatory** — apple and android cannot be marked done without them.

## 2. Engine / feature parameters (exact — do not invent)

| Parameter | Value / status |
|---|---|
| Break validation | Break registers **only** if wearable motion data shows physical movement **away from the screen**. |
| No-evidence rule (fail-safe) | Absence of wearable data ⇒ `BreakNotRegistered` (unverified). Absence is **never** movement. All ambiguity resolves to "break not registered"; the engine under-credits. |
| Verification authority | The verification decision is made **only on the backend**. watchOS (CoreMotion) / WearOS sensors collect and push evidence; the watch/phone/extension/desktop never mark a break complete. |
| Break state (categorical) | A break is `Registered` or `Not Registered` — binary. No "70% of a break," no movement-quality score surfaced to the user. |
| Motion ingest | Evidence arrives via shared sensor ingest (`POST /v1/sensors/motion/batches`), consumed by the pomodoro engine. |
| Interval lengths | **PENDING RATIFICATION — not spec-fixed.** Legacy precedent (also PENDING ratification): 25 min work / 5 min short break / 15 min long break, long break every 4 cycles, daily target 8. These are config values read through the Microkernel config/hook layer, **never hardcoded** in `core/`. |
| Qualifying-motion threshold | The quantitative signature of "movement away from the workstation" (steps/distance/duration/sensor-fusion recipe) is **PENDING DESIGN — backend-owned**; clients must not embed it. |
| Late-sync re-evaluation | Retroactive `BreakNotRegistered → BreakRegistered` on late-arriving evidence is allowed in principle; the exact re-evaluation window is **PENDING DESIGN**. |
| No-wearable degraded mode | Whether an honor-system mode exists is **PENDING DESIGN**; spec default is breaks simply do not register without wearable evidence. |
| Opinion 1 (categorical, not quantitative) | Binary break outcome only; no motion arithmetic surfaced. |
| Opinion 2 (no invented medical certainty) | No musculoskeletal/cardiovascular claims. AI does behavioral pattern analysis of sitting/break patterns only and always defers to "Consult your doctor." |
| Colors — Dark | `#1A1A1A` bg · `#00FF33` neon-green glow for the active timer · `#00CCFF` cyan buttons · `#FF3333` red alerts (glow effects). |
| Colors — Light | `#FFFFFF` bg · `#20A060` teal · `#0070A0` deep cyan (flat). |
| i18n | ICU Message Format, en/ar catalogs, full Arabic RTL with mirrored progress/countdown; no string concatenation. |

## 3. Session table

One row per session. `<date>` = ISO date the session runs; `<NN>` = zero-padded sequence. Surface order follows doc 13 §3/§4: **backend → web → mcp → chrome → apple → android → desktop**. The two shaded sub-sessions (`apple-watch`, `android-wear`) are **mandatory** gates on their parent surface.

| Session ID | Surface | Scope (what this one session builds) | Entry deps | Done criteria (doc 13 §5) | Plan docs to read |
|---|---|---|---|---|---|
| S-`<date>`-M8-backend-`<NN>` | backend | Pomodoro engine as its own ToGO plugin: pure `core/` state machine (`Idle → Working → BreakPending → BreakVerifying → BreakRegistered / BreakNotRegistered → Working`), `plugin.go` kernel registration (routes, hook subscriptions, config for interval lengths, migration manifest), expand-contract migration for session + break-verification tables, endpoints `POST /v1/engines/pomodoro/sessions`, `POST /v1/engines/pomodoro/sessions/{id}/stop`, `GET /v1/engines/pomodoro/state`, `GET /v1/engines/pomodoro/history`, plus consumption of `POST /v1/sensors/motion/batches`. Break verification consumes motion evidence only; no evidence ⇒ `BreakNotRegistered`. Interval lengths read from config (PENDING ratification), not hardcoded. | M0.1–M0.4 (kernel boot, TOGO auth + user isolation, plugin framework, migration harness). Shared sensor/motion ingest boundary (`/v1/sensors/...`) — build here or depend on X3 sensors ingest; resolve before starting. | **Backend-engine template:** plugin registered on kernel; migration expand-contract, every column NOT NULL with a default; endpoints return computed state; unit tests encode the one spec-fixed rule literally (break registers only on wearable movement; absence ≠ movement) and the config-driven interval values as parameters; integration test vs. real Postgres passes (idempotency replay, no-evidence ⇒ not registered, late-sync re-evaluation, break-window race); **no protocol constant appears outside the engine's `core/` package** (and interval numbers live in config, not `core/`). | 03 §8, §9.3; 04 §2–§9; 13 §3, §5 |
| S-`<date>`-M8-web-`<NN>` | web | Pomodoro focus card on the web dashboard (`/engines/pomodoro`, query key `['engine','pomodoro','current']`): renders server state (work/break-pending/verifying/registered/not-registered + today's registered breaks); start/stop mutations; countdown animates toward the **server-provided deadline only** and re-syncs on server response. Displays break outcome; never claims a break "counts." | M0.5–M0.7 (web shell + auth, theme engine, i18n engine); M8 backend. | **Client-engine template:** renders `/state` and submits start/stop intents only; **thin-client/SSOT audit passes** (no timer/interval/verification logic in client — grep finds no protocol constants; countdown is display-only against the server deadline); both themes render per the strict color spec (neon-green glow timer in dark); Arabic RTL mirrors correctly incl. progress/countdown; all strings ICU, none concatenated. | 05 §1, §3 (query keys/endpoints), §5 (Pomodoro Card); 03 §8; 13 §5 |
| S-`<date>`-M8-mcp-`<NN>` | mcp | `pomodoro_status` tool: **read-only** current work/break snapshot from `GET /v1/engines/pomodoro/state` via personal-access-token auth. Verify the deliberately **excluded** `complete_break` tool stays excluded — an AI must not fake a break. | M0.8 (MCP server + CLI skeleton); M8 backend. | **MCP template:** the tool calls the real API with PAT auth, returns server state verbatim, holds no protocol logic and no AI keys; the excluded `complete_break` (and any break-marking tool) stays excluded; tool schema description states outputs are behavioral tracking, not medical advice. | 11 (tool table + exclusions); 03 §8; 13 §5 |
| S-`<date>`-M8-chrome-`<NN>` | chrome | **Active Pomodoro Guard** (MV3, content-script-free): background worker mirrors work/break state to the toolbar **badge** from the aggregate snapshot poll; during a server-declared forced break it intercepts continuous browser activity (via chosen activity signal — `chrome.idle` vs. tab events, permission tradeoff PENDING) and **reports that activity to `POST /pomodoro/events` as sensor evidence only**. Never marks a break complete. | Chrome extension shell (auth/background worker/snapshot poll from the chrome foundation / M1 chrome session); M8 backend; motion+activity ingest live. | **Client-engine template:** badge renders server snapshot state; activity is reported as evidence, never a completion verdict; **thin-client/SSOT audit passes** (no interval/verification logic; the extension "never decides a break was completed"); both themes per color spec (alert `#FF3333` family while user keeps browsing during a break); Arabic RTL; ICU strings, none concatenated; permission set stays minimal unless the activity-signal decision explicitly widens it. | 06 §5 (Active Pomodoro Guard), §2.4 (permissions), §3.2; 03 §8; 13 §5 |
| S-`<date>`-M8-apple-`<NN>` | apple | iOS focus-timer screen; **macOS Workstation Guard** optional full-screen break overlay (reflects backend break state; lifts on server verdict; dismiss semantics PENDING DESIGN); **macOS menu-bar timer** (`MenuBarExtra`) rendering state + countdown from server timestamps. Renders server state only. Marking this surface **done requires the `apple-watch` sub-session below.** | Apple app shell + `HDAPIClient` + auth (apple foundation / M1 apple); M8 backend. | **Client-engine template** + **M8 gate:** surfaces render `/state` and submit start/stop only; **thin-client/SSOT audit passes** (no interval/verification logic; overlay never validates the break — closing it does not register one); both themes per color spec (glow timer; Metal-glow escalation is PENDING DESIGN); Arabic RTL incl. mirrored progress; ICU strings. **Not done until `apple-watch` sub-session is done** (doc 13 §4). | 07 §6–§7 (watchOS, menu bar, Workstation Guard); 03 §8; 13 §4–§5 |
| S-`<date>`-M8-apple-watch-`<NN>` | apple (watchOS) | **MANDATORY sub-session.** watchOS CoreMotion movement-evidence session: during a backend-declared break window, sample accelerometer/gyroscope, package as an evidence payload, and push to the ToGO API (via paired iPhone or direct — transport decision at build). Watch renders backend-returned state and **never self-completes a break.** | S-`<date>`-M8-apple-`<NN>`; M8 backend; the qualifying-movement **evidence contract** (backend-owned, PENDING DESIGN — payload = raw vs. featurized). | **Client-engine template** (sensor-collection variant): watch pushes evidence only and renders both `BreakRegistered` / `BreakNotRegistered` outcomes from the server; **SSOT audit passes** (no threshold/verification logic on-device); recorded-CoreMotion-trace test (movement vs. stationary) replayed vs. a mock API proves it reports evidence, renders both outcomes, and **never self-completes**; haptics timing originates from the server schedule; Arabic RTL on watch surfaces. Completing this **unblocks marking `apple` done.** | 07 §6.1 (CoreMotion break validation), §11 (Pomodoro test), Open Q #4; 03 §8; 13 §4 |
| S-`<date>`-M8-android-`<NN>` | android | Android phone focus-timer screen; **Android Auto** constraint-awareness surface for pomodoro state (display/voice per Auto affordances, backend-served ICU strings). Renders server state only. Marking this surface **done requires the `android-wear` sub-session below.** | Android app shell + core modules + auth (android foundation / M1 android); M8 backend. | **Client-engine template** + **M8 gate:** phone/Auto render engine-status snapshot and submit start/stop only; **thin-client/SSOT audit passes** (no interval/verification logic); both themes per color spec (Glance/Tile glow approximation PENDING DESIGN); full `LayoutDirection.Rtl` incl. mirrored progress; ICU strings shared with web catalogs, none concatenated. **Not done until `android-wear` sub-session is done** (doc 13 §4). | 08 §5–§6 (WearOS, Auto), §9 (RTL); 03 §8; 13 §4–§5 |
| S-`<date>`-M8-android-wear-`<NN>` | android (Wear) | **MANDATORY sub-session.** WearOS sensor movement-evidence session: during a backend-declared break window, sample the watch's motion/step sensors, transmit evidence to `POST /v1/engines/pomodoro/movement` (or the shared motion ingest); sense only inside break windows (battery). Backend decides; the Wear client **never registers a break** and embeds no threshold. | S-`<date>`-M8-android-`<NN>`; M8 backend; the movement **evidence contract / Wear transport** (backend-owned + phone-relay-vs-direct, PENDING). | **Client-engine template** (sensor-collection variant): Wear pushes evidence only, renders `BreakRegistered` / `BreakNotRegistered` as received; **SSOT audit passes** (no threshold/verification logic — the "engine decides" rule holds); test proves evidence is sent only during backend break windows and both verdicts render as received; no-wearable path leaves the break unregistered; Arabic RTL on Tiles/Wear surfaces. Completing this **unblocks marking `android` done.** | 08 §5.2 (Kinetic Pomodoro movement detection), §11, Open Q #3–#4; 03 §8; 13 §4 |
| S-`<date>`-M8-desktop-`<NN>` | desktop | Electron surface: **Workstation Lock / Break Enforcer** optional always-on-top overlay (one per connected display) driven by server break state, with a dismiss escape hatch (friction PENDING DESIGN); **system-tray live pomodoro countdown** (ticks locally against server anchors, re-anchors on sync) + quick-action menu; **global hotkey** for start/quick-log wired to API. Reuses the web focus card; has **no movement-sensor role**. | Desktop Electron shell + tray + hotkey + vault infra (desktop foundation / M1 desktop); M8 backend; web pomodoro card (S-`<date>`-M8-web-`<NN>`). | **Client-engine template:** overlay/tray/hotkey render server break state and submit intents only; **thin-client/SSOT audit passes** (overlay never validates a break — closing it, mouse, or keypress does not register one; only the server, on wearable movement, registers; no protocol constants in client code); both themes per color spec (`#00FF33` glow active timer, `#FF3333` alerts); Arabic RTL incl. mirrored progress in overlay + popover; ICU strings shared with web catalogs. | 09 §5 (tray), §7 (Break Enforcer), §8 (hotkeys), §10 (i18n/RTL); 03 §8; 13 §5 |

## 4. Notes & gotchas

- **Only one rule is spec-fixed.** "A break registers only on wearable-detected movement away from the screen, and absence of data never counts" is the whole spec (03 §8.2). Everything quantitative — interval lengths, break lengths, the motion threshold, long-break cadence, daily target — is an **open question / PENDING DESIGN**. Encode only the fail-safe rule as an engine invariant.
- **Interval lengths are PENDING RATIFICATION, not spec constants.** The legacy 25 min work / 5 min short / 15 min long / long break every 4 cycles / daily target 8 is a *precedent to reuse pending ratification*, not a value to hardcode. Keep it in Microkernel config/hook layer so a future doctor "Medical Restrictions" overlay (03 §10) can alter break policy without an engine rewrite. Do **not** let interval numbers leak into `core/` or any client (they would fail the thin-client / SSOT grep audit).
- **The watch/Wear sub-sessions are gates, not extras.** apple and android are **not done** until `apple-watch` (CoreMotion) and `android-wear` (Wear sensors) are done (doc 13 §4, §5). The phone/menu-bar/overlay/extension/desktop must never fabricate a break; a break with no motion evidence is `BreakNotRegistered`.
- **Fail-safe direction is under-credit.** Late-syncing evidence may upgrade `NotRegistered → Registered`; borderline/at-desk motion should *not* credit. All ambiguity resolves to "not registered."
- **Chrome liability.** The extension is content-script-free — it **cannot draw in-page overlays** and must intercept forced breaks via extension-owned surfaces (badge/notifications/side panel). Its activity signal is **evidence only**; the activity-signal mechanism (`chrome.idle` vs. tab events) and its permission tradeoff are PENDING (06 §5.2, §12).
- **Desktop liability.** The Break Enforcer is a full-screen always-on-top *app window*, not an OS lock; it must offer a dismiss path and has no sensor role. Multi-monitor coverage is first-class; Wayland global-hotkey and Linux tray support are open questions (09 §6–§8).
- **PENDING DESIGN carried by this module:** timer/overlay visuals and glow radii/intensities; how an unregistered break is communicated without shaming; Workstation Guard dismissal semantics (apple + desktop); chrome interception aggressiveness; the backend qualifying-movement evidence contract and re-evaluation window; watch↔phone transport for evidence; no-wearable degraded mode.
- **Motion ingest ordering.** The engine depends on the shared `/v1/sensors/...` motion ingest boundary. Confirm whether it is built inside the M8 backend session or supplied by X3 (sensors/biometrics) before starting backend; the watch/Wear sub-sessions cannot report evidence until it exists.

## 5. Handoff reminder

At the end of every M8 session, per doc 13 §6: write **one `session-handoff` memory** to the cabrain `healthdebug` brain (retain, then `memory/edit` to attach metadata — retain cannot set metadata) using the exact handoff content structure, with `source_ref: handoff/M8-kinetic-pomodoro/<surface>/<n>` (unique per session — never reuse) and `metadata` `{project:"healthdebug", category:"session-handoff", era:"greenfield", status:"current", surface:"<surface>", module:"M8", title:"Handoff <session-id>", next:"<module+surface>"}`. 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. `<surface>` values for this module: `backend`, `web`, `mcp`, `chrome`, `apple`, `apple-watch`, `android`, `android-wear`, `desktop`.
