Omniscio documentation
Browse all documentation
  1. Getting Started13
  2. Sessions & Agents115
  3. Inbox & Notifications59
  4. Projects & Tasks95
  5. Automation & Scheduling75
  6. Knowledge & Memory26
  7. AI Features60
  8. Integrations100
  9. Plugins & Marketplace33
  10. Cloud & Teams56
  11. Settings & Customization58
  12. Account & Billing28
  13. Troubleshooting84
  14. CLI & API Reference22
  15. Legal & Policies4
  16. Uncategorised22

Pomodoro

The built-in pomodoro timer: focus blocks and breaks, presets, schedules that auto-start at a wall-clock time, and the today/week/by-project stats. Covers where the Pomodoro tab lives, how a run advances and pauses, how it pairs with Focus Mode, and what it deliberately does not do.

What it is

A built-in pomodoro timer that runs focus blocks (default 25 min) followed by breaks (5 min short, 15 min long every 4th cycle). You pick a preset — a named bundle of those durations plus alarm overrides and an optional default project — click Start, and Omniscio drives a single active run from focus → short-break → focus → … → long-break → … → complete, chiming at each phase change.

The feature is paired with the Alarms virtual project in the Omniscio sidebar group, accessed via the Pomodoro tab of the Alarms project's pane-2 tab control.

When the (in-development) Time Tracker is enabled, each completed focus session also appears on its unified timeline as a focus block — a one-way projection that leaves the Pomodoro engine untouched. See time-tracker.md.

Two side-projects on top of "just start a timer":

  • Schedules auto-start a preset at a recurring wall-clock time ("every weekday at 9 AM, start the Deep Work preset").
  • Stats roll up the focus seconds into Today / This Week / by project, with a "planned vs actual" today block.

The whole feature is local-only — nothing leaves your machine, no AI is involved at any layer, no cost. The only cross-feature integration is with Alarms (shared sound picker, focus-mode pierce, foregrounding) and with Projects (the optional project attribution).

Where to find it

The Pomodoro feature is rendered inside the Alarms virtual project, not as its own sidebar row. Open the Alarms virtual project in the Omniscio sidebar group, and at the top of pane 2 there is a tab control with Alarms / Pomodoro tabs — click Pomodoro to switch the rest of the project panel to the pomodoro UI.

Inside the Pomodoro tab:

  • Pane 2 (sidebar) — the preset list with a per-row Play button for quick-start. Header has three icon toggles: a calendar-clock icon (Schedule editor), a history icon (History view), and a gear (Settings panel). Clicking a preset row selects it and loads its editor into pane 3; clicking Play on a row starts a run immediately using that preset's default project (if set).
  • Pane 3 (detail) — when a preset is selected, the preset editor: name, focus minutes, short-break minutes, long-break minutes, cycles-until-long-break, total cycles (or "unlimited"), default project (dropdown of all your Omniscio projects + "Unassigned"), and an Advanced section with the per-preset alarm overrides (sound, modal vs. banner assertiveness, bring-to-foreground, pierce-focus-mode). When the Schedule toggle is on, pane 3 swaps to the Schedule editor (list of recurring rows, each with day-of-week chips + HH:MM time + preset picker + project picker + label + enabled toggle); when the History toggle is on, pane 3 swaps to the History view (today / week / by project rollup + a planned-vs-actual block). When a run is active, pane 3 also shows the Session view above the editor: big countdown, phase label, Pause / Resume, Skip Phase, and Stop buttons. Skip Phase advances immediately to the next phase in the ladder (focus → break, break → focus) without waiting for the countdown to reach zero; it counts the phase as completed (focus seconds for the phase are added even though you skipped early).

The "Pomodoro is a tab of Alarms" layout is deliberate — it keeps both notification-surface features (timers + reminders) under one cognitive heading and lets them share the same sound picker, foregrounding rules, and focus-mode interplay without duplicating settings UI.

How it behaves

How to start a run

  1. Open the Alarms virtual project in the Omniscio sidebar.
  2. Click the Pomodoro tab at the top of pane 2.
  3. Click the Play button on any preset row in pane 2 (or select the preset and click Start in pane 3). The run begins immediately — pane 3 swaps to show the Session view with a live countdown.

If the preset has a Default project set, the run is attributed to that project automatically (stats roll up under it). You can clear the default project on the preset to be prompted/unassigned, or override per-run by starting from a different preset.

Phase transitions

The active run advances itself. A 1-second background tick checks whether the current phase has ended; when it has, Omniscio transitions atomically and emits a push to the renderer, which:

  1. Plays the chime for the resolved sound (per-preset override or the Alarms-default sound).
  2. Shows a transition toast with the new phase label and remaining time.
  3. Brings the Omniscio window to the foreground only if both layers say so — see "Foregrounding" below; the default is no for phase changes.

The phase ladder is:

  • focus → short-break when cyclesCompleted % cyclesUntilLongBreak !== 0.
  • focus → long-break when cyclesCompleted % cyclesUntilLongBreak === 0.
  • break → focus always (the cycle counter advances on the focus → break edge, so the break never re-counts).
  • focus → complete when cyclesCompleted >= totalCycles (the preset's totalCycles is nullable — null means unlimited, so the run only ends when you click End).

Pause / resume / skip / stop

The Session view has four controls below the countdown:

  • Pause freezes the countdown without losing time. Pause records the moment you paused in memory; Resume extends phase_ends_at by the pause duration and persists the new value, so the remaining time after resume matches what you saw when you paused.
  • Skip Phase advances immediately to the next phase (no confirmation — it's a discrete state transition). The skipped phase is counted as completed; its focus-seconds (if it was a focus phase) are still added to the stats rollup.
  • Stop ends the entire run after a confirm dialog (to avoid accidental loss of cycles).

Caveat: pause state is in-memory. If you close Omniscio while a run is paused, the run is reaped as crashed on the next boot — pausing isn't crash-safe. This trade-off avoids a paused_at DB column for what is normally a few minutes of pause; the alternative is to End the run before closing Omniscio.

Foregrounding (an important deliberate split)

Three settings interact at phase-change time:

Setting Default What it does
pomodoroPhaseForeground false Whether ANY phase change can pull Omniscio's window to the foreground.
alarmsBringToForeground true The Alarms feature's foregrounding default (NOT consulted here).
per-preset bringToForegroundOverride null When non-null, overrides pomodoroPhaseForeground for THIS preset.

The deliberate split: a 5-minute focus block ending should chime + show a tray badge but not yank you out of whatever you were doing — so the pomodoro default is false. The alarm default is true (an alarm exists precisely to interrupt). Per-preset bringToForegroundOverride is the escape hatch when you want a particular preset to pierce.

If you change alarmsBringToForeground you do not change pomodoro behavior. They're independent, and the Settings UI labels both clearly.

Focus Mode integration (auto-silence during focus blocks)

A pomodoro run can automatically enable Focus Mode — the toolbar bell pill that batches inbox notifications — while you're in a focus block, then release it during breaks. The intent is to pair "I'm starting a focus timer" with "stop pinging me about every push notification" without forcing you to remember to click the bell manually.

The setting

Settings → Notifications → Alarms → Auto-Enable Focus Mode During Focus Blocks (pomodoroAutoEnableFocusModeDefault, default on) is the global default. Every per-preset Auto-enable Focus Mode override (Advanced section of the preset editor) is a tri-state:

Override value What it means
null (default) Inherit the global pomodoroAutoEnableFocusModeDefault.
true This preset always auto-enables Focus Mode, even when the global default is off.
false This preset never touches Focus Mode, even when the global default is on.

When Focus Mode flips during a run

When the run is in a state that warrants silence — actively in a focus phase — Focus Mode is on. Any other state (break, paused, ended) drops it. Concretely:

  • Start a run → if a focus phase is the opening phase (it always is), Focus Mode is enabled.
  • focus → short-break or focus → long-break → Focus Mode is disabled (you get your alerts back).
  • break → focus (start of the next cycle) → Focus Mode re-enables.
  • Pause during a focus phase → Focus Mode drops (pause is treated as a break — you're stepping away, so let notifications through).
  • Resume after a pause → Focus Mode re-enables only if the run resumes into a focus phase.
  • End the run (manually or by reaching totalCycles) → Focus Mode drops back to whatever it was before the run started.
  • Omniscio crashes / closes → because Focus Mode state is settings-persisted and the run row is marked crashed on the next boot, there's no orphaned Focus Mode lock. If you had Focus Mode on for an unrelated reason before the run, that state is restored.

Manual-toggle protection

If you click the bell icon manually at any point during a managed run — either turning Focus Mode on when the run had dropped it, or off when the run had it enabled — Pomodoro stops managing Focus Mode for the rest of that run. The reasoning: your manual click is a deliberate statement ("actually, I want it the other way right now"), and the timer shouldn't override you on the next phase transition. The block lasts only for the current run; the next run starts fresh.

This is wired through the Pomodoro service's subscription to focusModeService's change listener (onFocusModeExternalChange in src/main/services/pomodoro-service.ts). Every change carries a source, and the listener ignores its own emissions (event.source === 'pomodoro'), so a programmatic enable/disable from the Pomodoro service itself never releases its own claim; any other source — the bell-icon click (src/main/ipc/focus-mode-handlers.ts), the system, or startup — does.

Per-preset escape hatches

The per-preset Auto-enable Focus Mode override is the way to opt a single preset out (or in) without flipping the global default. Common shapes:

  • "Deep Work" preset — override = true. Always silences, even if you've globally disabled the integration.
  • "Quick admin pomodoro" preset — override = false. Stays notification-permissive even when you've turned the integration on globally.
  • All other presets — override = null. Follows the global default.

Schedules — auto-start at a wall-clock time

A schedule is a recurring row that calls Start for you. Open the Schedule editor from pane 2's calendar-clock icon, and add rows of:

  • Day of week — chips for Sun / Mon / … / Sat; pick one day per row.
  • Start time — HH:MM in your local timezone.
  • Preset — dropdown of all your presets.
  • Project (optional) — overrides the preset's default project for this schedule. Empty = use the preset's default. Set explicitly to "Unassigned" to opt out per-schedule.
  • Label (optional) — surfaces in the run row and stats so you can tell apart "Mon 9am — Deep Work" from "Wed 9am — Writing".
  • Enabled — toggle to mute the schedule without deleting it.

How the resolver works

A 1Hz background tick (the same one that drives phase transitions) evaluates every enabled schedule for the current minute. For each match:

  1. 30 seconds before the scheduled minute, Omniscio shows a pre-start toast ("Pomodoro starting in 30s — Deep Work") so you can dismiss it or just be ready. Toasts are per-(schedule, minute) so a re-render doesn't double up.
  2. At the scheduled minute exactly, Omniscio atomically claims the minute and calls Start. If another run is already active (you manually started one, or the previous schedule's run hasn't ended yet), the new schedule is skipped — manual starts always win, no displacement.
  3. Idempotency: a DB column last_fired_minute on the schedule row stores the last minute it fired. The atomic SQL UPDATE that claims a minute also bumps this column, so two concurrent ticks (or a restart mid-minute) can never both fire the same schedule. Lose-on-restart never happens — if Omniscio crashed at 8:59:30, the resolver after restart at 9:00:05 still fires the 9 AM schedule, because last_fired_minute < 540 (9 AM in minute-of-day). Resume in the same minute = idempotent skip.

The master kill switch

Settings → Notifications → Alarms → Pomodoro: Enable schedules (pomodoroScheduleEnabled, default on) controls whether the resolver runs at all. When off, no pre-start toasts, no auto-starts, no schedule-related DB reads — but manually-started runs continue ticking normally. This is the "silence the schedule" toggle when you want to keep your preset library and schedule rows intact but stop the auto-fires (a vacation week, a deadline crunch, etc).

Individual schedule rows also have their own enabled toggle so you can silence just one without flipping the master.

Stats — Today, This Week, by Project

Open the History view from pane 2's history icon. Three rollups:

  • Today — sum of focus_seconds_total across runs started today, in your local timezone (resets at local midnight). The engine increments focus_seconds_total on every focus-phase completion, so a run you stopped mid-break still contributes the focus seconds from the phases that finished before you stopped. Phases skipped via Skip Phase also count as completed.
  • This Week — same sum since the most recent Monday 00:00 local.
  • By Project — table with rows for each projectId that has any focus seconds this week, sorted DESC by weekly total. Rows include today and weekly columns. Runs with projectId: null aggregate under an "Unassigned" bucket (never silently dropped — see invariant I6 in the contract).

Planned vs actual

Above the rollups is a "Today's planned vs actual" block:

  • Planned sums today's enabled schedules × their preset's focusMinutes. E.g. 4 schedules today at 25 min each = 100 planned focus minutes.
  • Actual is the same number as the "Today" rollup (completed focus seconds, converted to minutes).

This is a rough target — the planned number is what you'd accomplish if you ran every scheduled session to completion. Schedules that haven't fired yet still count toward "planned"; if you delete or disable a schedule, it drops out of planned immediately. Soft-deleted presets and disabled schedules are both excluded.

Presets — what you can customize

Each preset row in pane 2 corresponds to a row in pomodoro_presets. The editor lets you set:

Field Default Notes
Name "Default" Free text. Shown in the sidebar list, schedule rows, history.
Focus minutes 25 The focus phase duration.
Short-break minutes 5 The short-break phase duration.
Long-break minutes 15 The long-break phase duration (every Nth cycle).
Cycles until long break 4 After this many focus-block completions, the next break is long.
Total cycles null null = unlimited (only ends on End). Number = run completes at N.
Default project null Pre-fills the run's projectId when Start is clicked from this preset.
Auto-start breaks true Editor toggle, currently inert — see "Known gap" below.
Auto-start focus false Editor toggle, currently inert — see "Known gap" below.
Sound override (Advanced) null Per-preset chime; null = inherit alarmsDefaultSound.
Assertiveness (Advanced) null modal / banner / silent-banner; null = inherit.
Bring-to-foreground (Advanced) null null = inherit pomodoroPhaseForeground (default false).
Pierce focus-mode (Advanced) null null = inherit alarmsPierceFocusMode (default false).
Auto-enable Focus Mode (Advanced) null null = inherit pomodoroAutoEnableFocusModeDefault (default on).

The null = inherit pattern means a single change at Settings → Notifications → Alarms propagates to every preset that didn't override, while presets that did override are immune (intentionally so — you set them up that way).

Presets soft-delete (is_deleted = 1) when removed, so any run rows that reference them in stats keep working. A soft-deleted preset is silently dropped from the sidebar, the schedule's preset dropdown, and the planned rollup.

Known gap — Auto-start toggles are inert

The Auto-start breaks and Auto-start focus toggles render in the preset editor (and persist to pomodoro_presets.auto_start_breaks / auto_start_focus), but the engine's transition() in src/main/services/pomodoro-service.ts doesn't read them — every phase change unconditionally starts the next phase. In other words, both toggles behave as if they were always on for breaks and always on for focus. Flipping either toggle changes the stored value but produces no observable behavior. This is a UI/engine wiring gap, not a feature decision — track it as such if you see it.

Settings

All Pomodoro settings live under Settings → Notifications → Alarms (the section is shared with Alarms; the Pomodoro subsection groups its own toggles). Three settings are exclusive to Pomodoro:

  • pomodoroScheduleEnabled (default on) — the master kill switch described above. Hides nothing in the UI; only stops the resolver.
  • pomodoroPhaseForeground (default off) — whether phase-change transitions can pull Omniscio to the foreground. Default off because a focus block ending shouldn't yank you out of your deep-work window.
  • pomodoroAutoEnableFocusModeDefault (default on) — whether a focus block automatically enables Focus Mode for the duration of that phase (releases on breaks / pause / end). See "Focus Mode integration" above for the per-run lifecycle and manual-toggle protection.

Three settings are inherited from Alarms when a preset has the matching override set to null:

  • alarmsDefaultSound → preset's soundId
  • alarmsAssertiveness → preset's assertivenessOverride
  • alarmsPierceFocusMode → preset's pierceFocusModeOverride

(alarmsBringToForeground is NOT inherited — see "Foregrounding" above.)

What it deliberately doesn't do

  • No cross-device sync. Stats and runs are local SQLite. If you run Omniscio on two machines, they each have their own pomodoro history.
  • No automatic break enforcement. If you skip a break by manually ending the run mid-break and starting a fresh focus, the engine doesn't try to stop you. The chime + tray badge are the nudge; the rest is on you.
  • No "tomato" gamification. No streaks, no leaderboards, no badges. The stats are a usage record, not a habit-tracking layer.
  • No mobile pomodoro controls. The Pomodoro tab is desktop-only. The mobile sidebar shows the Alarms virtual project, and Alarms surfaces are still reachable on phone — but starting / pausing / configuring presets and schedules is desktop only.
  • No project picker per-run. The Start button always uses the preset's default project (or no project, if unset). To change per-run project, edit the preset's default or set a schedule with an override.
  • No tray-icon countdown overlay. The tray badge updates on phase changes, but the seconds count down only in the Session view inside Omniscio.

Where to look when it goes wrong

  • A schedule didn't auto-start at the scheduled minute — first check Settings → Notifications → Alarms → Pomodoro: Enable schedules is on (the master kill switch). Then check the schedule row's enabled toggle. Then check whether a different run was already active at the scheduled minute (manual starts win — schedules don't displace active runs). Finally check the schedule's day-of-week — Mon=1, …, Sun=0 in storage.
  • The pre-start toast fired but the auto-start didn't — most likely a run was already active. The toast fires unconditionally in the 30s window; the actual auto-start checks getActiveRun() first.
  • A phase change played the wrong sound — the resolution is per-preset soundId (non-null) → alarmsDefaultSound (Settings → Notifications → Alarms). If the preset's soundId is non-null, the global default is bypassed.
  • Omniscio pulled itself to the foreground when I didn't want it to — check the preset's bringToForegroundOverride. If non-null, it's overriding pomodoroPhaseForeground. Set it back to null to inherit the (off) default.
  • Focus Mode didn't enable when my focus block started — first check Settings → Notifications → Alarms → Auto-Enable Focus Mode During Focus Blocks is on. Then check the preset's per-preset Auto-enable Focus Mode override — if it's false, this preset is opted out. If you toggled the bell icon manually during the run, the service stops managing Focus Mode for the rest of that run (deliberate — your click wins); restart the run to re-enable management.
  • Focus Mode stayed on after my run ended — only happens if you toggled the bell manually during the run (the service handed control back to you and didn't drop it on end). Click the bell to disable.
  • My stats look wrong — the Today rollup uses your local timezone midnight; the Week rollup uses the most recent local Monday 00:00. If you've recently changed timezones, "today" can include yesterday's runs (or vice versa). The rollup queries are in src/main/db/queries-pomodoro.ts — search for getPomodoroStats.
  • An active run survived an Omniscio restart and now nothing can start — this shouldn't happen because reapStaleActiveRuns() runs at service boot and marks any leftover active/paused rows as end_reason = 'crashed'. If it does, manually flipping the row's ended_at in the DB clears the lock, but file a bug — the reaper has a hole.

For agents

CLI access

Pomodoro is fully reachable from the CLI control server (127.0.0.1:19519), so an external agent or script can drive a run, edit your presets and schedules, and read your stats headlessly:

  • Runs — POST /pomodoro/runs/start, POST /pomodoro/runs/:id/pause, POST /pomodoro/runs/:id/resume, POST /pomodoro/runs/:id/skip-phase, POST /pomodoro/runs/:id/stop, GET /pomodoro/runs/active, GET /pomodoro/runs/history.
  • Presets — GET / POST /pomodoro/presets, PATCH / DELETE /pomodoro/presets/:id, POST /pomodoro/presets/:id/set-default.
  • Schedules — GET / POST /pomodoro/schedules, PATCH / DELETE /pomodoro/schedules/:id, POST /pomodoro/schedules/:id/toggle.
  • Stats — GET /pomodoro/stats (Today / This Week / by-Project).

Two of those are DELETEs, so an agent you point at this family can remove a saved preset or schedule. The routes are registered unconditionally (pomodoro has no unreleased-feature flag) and take the usual CLI bearer token.

The desktop/mobile gap is in the UI, not the API: the Pomodoro tab itself does not render on a paired phone, as noted above — but the routes above answer headlessly either way, and GET /pomodoro/stats is the supported way to read those numbers rather than opening the local SQLite file.

Architecture pointers (for code edits)

If you're editing the feature, read the contract first: .claude/memory/contracts/pomodoro-contract.md — it names the test that locks each invariant, the state machine, and the "changing the engine" checklist.

The major files are:

  • DB schema migration v230 in src/main/db/database.ts — adds the project_id column to pomodoro_runs, the default_project_id column to pomodoro_presets, and the pomodoro_schedules table.
  • DB queries in src/main/db/queries-pomodoro.ts — every CRUD operation, claimScheduleMinute() for the atomic firing gate, getPomodoroStats() for the rollups.
  • Service in src/main/services/pomodoro-service.ts — the 1Hz tick, phase transitions, schedule resolver, and the foregrounding resolution.
  • IPC handlers in src/main/ipc/pomodoro-handlers.ts.
  • Renderer store in src/renderer/src/stores/pomodoro-store.ts — caches presets, schedules, stats, active run; subscribes to pushes.
  • Renderer UI under src/renderer/src/features/alarms/ — PomodoroSidebar.tsx, PomodoroPresetEditor.tsx, PomodoroScheduleEditor.tsx, PomodoroSessionView.tsx, PomodoroHistoryView.tsx, PomodoroPhaseTransitionToast.tsx, PomodoroSchedulePrestartToast.tsx.

Related

Alarms is the paired feature in the same virtual project, sharing the sound picker, the foregrounding helper, and the focus-mode pierce. Focus Mode is the batching gate that pomodoro chimes route through, and a preset's Pierce Focus Mode is the bypass around it. And Use Recipes covers recipes, which run on agent time rather than wall-clock time — if you want "every Monday at 9 AM, spawn an agent" instead of "every Monday at 9 AM, start a focus timer", use a cron job, not a pomodoro schedule.

Last verified 2026-10-06