diff options
Diffstat (limited to 'docs/superpowers/specs/2026-07-15-task-labels-and-projects-design.md')
| -rw-r--r-- | docs/superpowers/specs/2026-07-15-task-labels-and-projects-design.md | 69 |
1 files changed, 69 insertions, 0 deletions
diff --git a/docs/superpowers/specs/2026-07-15-task-labels-and-projects-design.md b/docs/superpowers/specs/2026-07-15-task-labels-and-projects-design.md new file mode 100644 index 0000000..4144d5b --- /dev/null +++ b/docs/superpowers/specs/2026-07-15-task-labels-and-projects-design.md @@ -0,0 +1,69 @@ +# Task Labels and Projects — Design + +**Status:** Backlog idea, refined via interview 2026-07-15. Not yet approved for implementation. + +## Context + +doot-native tasks have had a `Labels []string` field on `models.Task` (`internal/models/types.go`) since early in the project — it's fully wired at the store layer (persisted as a JSON column, scanned back out by `scanNativeTasks`) but never surfaced in any UI, web or widget. There's no editing, no display, no filtering. + +There's also no real "project" concept for doot-native tasks today. `Task.ProjectID`/`ProjectName` exist on the struct and `ProjectName` is a real column in `native_tasks`, but a repo-wide search turns up **zero** places that ever set `ProjectName` to a non-empty value when constructing a native task — it's vestigial, always empty in practice. This is likely a leftover from the Todoist-sync era (Todoist tasks did have real project names; the field just never got repurposed after native tasks replaced Todoist in commit `945c345`). So this feature isn't "finish wiring up projects" — it's building projects from scratch, alongside finally wiring up labels. + +This is a foundational feature: [[doot-future-task-scheduling-ideas]] items 1 (budgets/availability) and 4 (task chains) both reference "projects" as a dependency. Building this first unblocks both. + +## Scope + +doot-native tasks only, matching every other feature built this session. Trello/Google Tasks cards keep whatever labeling concept they already have server-side (Trello lists, etc.) — this feature does not touch them. + +## Data Model + +**Labels** — reuse the existing field, no schema change needed: +- `Task.Labels []string`, already a JSON column on `native_tasks`. +- Free-form text tags, many-to-many with tasks (a task can have any number of labels). +- User-assignable color per label. Since labels are currently just strings with no identity beyond their text, color needs a home: a new small `labels` table (`name TEXT PRIMARY KEY, color TEXT`) mapping label text → color hex. A label typed on a task that doesn't yet exist in this table gets a default color (or prompts for one) the first time it's colored. + +**Projects** — new, lightweight: +```sql +CREATE TABLE projects ( + id TEXT PRIMARY KEY, + name TEXT NOT NULL, + color TEXT NOT NULL, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + archived BOOLEAN DEFAULT 0 +); +``` +- `native_tasks` gets a new `project_id TEXT DEFAULT ''` column (a real FK-shaped reference, not the vestigial `project_name` string column — that column stays as dead weight for now since dropping SQLite columns needs a table-rebuild migration, out of scope for this feature; it just stops being read). +- Exactly one project per task (`project_id` is a single string, empty = unassigned), per the interview answer. Labels handle the cross-cutting many-to-many need instead. +- A project is not itself an atom/task — it's pure metadata (name + color) that a task points to. + +## Recurrence Interaction + +Both `project_id` and `Labels` are **series-level metadata**: `CreateNextIteration` (in `internal/store/native_tasks.go`) already copies `content`, `description`, `project_name`, `priority`, and `labels` onto every new occurrence — `project_id` joins that same copy-forward list, no new mechanism needed. This matches the interview's "inherit automatically" answer exactly. + +## API + +New endpoints, doot-only, matching the existing `/api/widget/task/*` pattern: +- `GET /api/widget/projects` — list all non-archived projects (id, name, color), for populating a picker. +- `POST /api/widget/projects` — create a project (name, color). +- `POST /api/widget/task/project` — `{id, project_id}`, sets or clears a task's project. +- `POST /api/widget/task/labels` — `{id, labels: []string}`, replaces a task's label set (matches `UpdateNativeTask`'s replace-whole-value style rather than an add/remove-single-label API). +- Labels don't need their own CRUD endpoint in the same way projects do — a label is just a string until someone assigns it a color, so `POST /api/widget/task/labels` implicitly "creates" any new label text. A separate `POST /api/widget/labels/color` — `{name, color}` — sets a label's color. + +## UI + +**Widget (Android):** per the interview, minimal — a small color mark on the time-grid row (project color takes priority for the row's accent; if labels need visual representation too, a second smaller dot, but start with just project color to avoid clutter). No filtering UI on the widget itself. + +**Task detail popup (`TaskDetailActivity`):** full display and editing — a project picker (chip showing current project, tap to open a picker/create-new dialog) and a label editor (chip-entry, similar in spirit to the recurrence weekday chips but free-text with autocomplete against existing label names), both using the color-coding. + +**Web:** out of scope for this spec's UI section — the interview didn't ask about web display specifically; assume parity with the widget's popup-level editing is desirable but not designed here. + +## Testing + +Standard TDD per this project's convention: store-layer tests for `CreateProject`, `GetProjects`, `SetTaskProject`, `SetTaskLabels`, `SetLabelColor`; handler tests for the new endpoints (mirroring `widget_test.go`'s style); a `CreateNextIteration` test confirming `project_id` and `Labels` both carry forward to the next occurrence. + +## Out of Scope + +- Project hierarchies (sub-projects, nesting) — flat list only. +- Multi-project tasks — exactly one project per task, per the interview. +- Archived-project handling in the UI beyond hiding them from the picker (no dedicated archive-management screen designed here). +- Web UI specifics (noted above). +- Migrating/backfilling the vestigial `project_name` column — it's simply superseded, not cleaned up, in this spec. |
