summaryrefslogtreecommitdiff
path: root/docs/superpowers/specs/2026-07-15-task-budgets-and-availability-design.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/superpowers/specs/2026-07-15-task-budgets-and-availability-design.md')
-rw-r--r--docs/superpowers/specs/2026-07-15-task-budgets-and-availability-design.md64
1 files changed, 64 insertions, 0 deletions
diff --git a/docs/superpowers/specs/2026-07-15-task-budgets-and-availability-design.md b/docs/superpowers/specs/2026-07-15-task-budgets-and-availability-design.md
new file mode 100644
index 0000000..923ef54
--- /dev/null
+++ b/docs/superpowers/specs/2026-07-15-task-budgets-and-availability-design.md
@@ -0,0 +1,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.