# Task Budgets and Availability Times — Design **Status:** Implemented (migration 025, shipped 2026-07-17). Status header and the implementation plan's checkboxes were never updated after implementation -- corrected 2026-07-17. ## Context The goal is to know whether what's on your plate actually fits in the time you realistically have, without doot trying to auto-schedule your day for you. This is explicitly a **visibility** feature, not a **scheduling** one, per the interview. **Depends on:** [[doot-future-task-scheduling-ideas]] item 2 (task labels/projects) — availability tracking is opt-in per project/label, so that feature needs to exist first. ## Scope doot-native tasks only. Opt-in: a project or label is marked as "budget-tracked," and only tasks under a tracked project/label count against any budget calculation. Untracked tasks are invisible to this whole feature — the default experience is unchanged. ## Data Model **Availability** — a manual recurring weekly template, reduced by real calendar events: ```sql CREATE TABLE availability_blocks ( id TEXT PRIMARY KEY, weekday INTEGER NOT NULL, -- 0-6, Sun-Sat start_time TEXT NOT NULL, -- "18:00" end_time TEXT NOT NULL, -- "20:00" label TEXT DEFAULT '' -- optional, e.g. "evening focus time" ); ``` At computation time (see below), a day's available minutes = sum of that weekday's `availability_blocks` minus any overlapping existing calendar events (already fetched via `BuildTimeline` — no new calendar integration needed, just a subtraction pass over blocks the timeline already has). **Budget** — both per-task estimate and a period pool, per the interview: - `native_tasks` gets `estimated_minutes INTEGER DEFAULT 0` — user-entered or inferred (see below). - A period pool isn't a separate stored number — it's *computed* as the sum of `availability_blocks` for the period (today, this week), and compared against the sum of `estimated_minutes` for all budget-tracked, incomplete, due-in-period tasks. No new "pool" table; the pool is availability itself. **Estimate inference ("learned from history"):** requires tracking actual completion duration, which doot doesn't currently record (tasks have `due_date`/`completed_at` but no "started working on this" timestamp). The simplest version that doesn't require a new "time tracking" UI: when a task completes, if it had no `estimated_minutes` set, backfill nothing (no signal to learn from) — but if a *label* or *project* accumulates several tasks with user-entered `estimated_minutes`, use the average of the same-label/project tasks' user-entered estimates as the default for a *new* task under that label/project, rather than inferring from actual elapsed time. This sidesteps needing real time-tracking infrastructure while still delivering "the system learns typical durations for this kind of task." True elapsed-time-based learning is a larger, separate feature not designed here. ## Computation and Surfacing No new background job — this is computed on read, at the same points `BuildTimeline`/`HandleWidgetGet` already run: 1. For "today" and "this week," sum `estimated_minutes` across budget-tracked incomplete tasks due in that window. 2. Sum available minutes from `availability_blocks` for the matching weekdays, minus overlapping calendar events already in the timeline. 3. If load > availability, surface a flag — not a hard block, not a reorder. Likely surfaced as a small indicator in the web timeline (e.g. "6.5h scheduled / 4h available today") and, if there's room, a small badge on the widget's TODAY header. Exact placement is a UI-design decision for the implementation plan, not fixed here. Per the interview: **no auto-scheduling, no auto-reprioritization** — the flag is purely informational. ## API - `GET/POST /api/widget/availability` — CRUD for `availability_blocks`. - `POST /api/widget/task/estimate` — `{id, estimated_minutes}`. - The overflow flag itself doesn't need a dedicated endpoint — it's computed as part of the existing `GET /api/widget` and the web timeline's existing data-building path, added as a new field on the response (e.g. `budget_status: {scheduled_minutes, available_minutes}` alongside `items`). ## Recurrence Interaction `estimated_minutes` is task-level, not series-level — the interview didn't ask about this directly, but since it's meant to reflect "how long does *this kind* of task take," and `CreateNextIteration` already copies most fields forward, the natural default is to carry it forward like content/description/project/labels. Re-estimating a specific occurrence would just mean editing that occurrence's value directly (no override mechanism needed beyond the normal edit UI). ## Testing Store-layer tests for availability CRUD and the availability-minus-calendar-events computation (a pure function, easy to unit test with fixture events). Handler tests for the new endpoints and for the `budget_status` field appearing correctly in `/api/widget`'s response only when budget-tracked tasks exist. ## Out of Scope - Auto-scheduling tasks into specific time slots. - Auto-reprioritization or auto-deferral on overflow. - Real elapsed-time tracking (start/stop timers) — estimate inference uses same-label/project averaging of user-entered values instead. - Any UI beyond a basic indicator — exact visual treatment is an implementation-time decision. - Non-project/label-scoped (global) budgets — everything is opt-in per the interview.