summaryrefslogtreecommitdiff
path: root/docs/superpowers/specs/2026-07-15-task-budgets-and-availability-design.md
blob: 923ef54889b410337b7032bdaad877f32f563f57 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# Task Budgets and Availability Times — Design

**Status:** Backlog idea, refined via interview 2026-07-15. Not yet approved for implementation.

## 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.