diff options
| author | Peter Stone <thepeterstone@gmail.com> | 2026-07-17 22:22:37 +0000 |
|---|---|---|
| committer | Peter Stone <thepeterstone@gmail.com> | 2026-07-17 22:22:37 +0000 |
| commit | b007fee8fb5b39a5f9b369c59af71ac9e795ceaf (patch) | |
| tree | a5b999b8f4c23a52ae9249675753a92a77993008 /internal/models | |
| parent | 70e6dd75130e70f2db83096c23eaa75326b183a2 (diff) | |
Implement linear task chains and recurring maintenance buckets
Backend, web timeline, and Android widget wiring for the last two
unimplemented items from doot-future-task-scheduling-ideas.
Chains: task_chains table + chain_id/chain_position/chain_unlocked on
native_tasks (migration 026), WIP-limit-1 advancement hooked into
CompleteNativeTask, locked tasks excluded from all date-based queries,
5 new /api/widget/chains* endpoints, an N/M position badge on web and
Android widget rows.
Buckets: maintenance_buckets table + bucket_id/bucket_state/
bucket_last_active_at on native_tasks (migration 027),
staleness-then-priority selection scoring, a new RunBucketCycleCheck
scheduler loop, 5 new endpoints including the distinct Defer action, a
Defer button on web and Android widget rows.
Also corrected stale "not yet approved" status headers on the two
already-shipped specs this work depended on (labels/projects, budgets/
availability) -- their headers were never updated after implementation.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EZ7ikw2ukGJFTHE3bJS7zL
Diffstat (limited to 'internal/models')
| -rw-r--r-- | internal/models/timeline.go | 3 | ||||
| -rw-r--r-- | internal/models/types.go | 33 | ||||
| -rw-r--r-- | internal/models/widget.go | 3 |
3 files changed, 39 insertions, 0 deletions
diff --git a/internal/models/timeline.go b/internal/models/timeline.go index 1313712..4b90856 100644 --- a/internal/models/timeline.go +++ b/internal/models/timeline.go @@ -45,6 +45,9 @@ type TimelineItem struct { ListID string `json:"list_id,omitempty"` // For Google Tasks RecurringEventID string `json:"recurring_event_id,omitempty"` // For calendar events ProjectColor string `json:"project_color,omitempty"` // For doot-native tasks with a project assigned + ChainPosition int `json:"chain_position,omitempty"` // For doot-native tasks that belong to a linear chain (1-indexed for display) + ChainTotal int `json:"chain_total,omitempty"` // Total tasks in the chain, paired with ChainPosition + BucketState string `json:"bucket_state,omitempty"` // "active" for doot-native tasks that are a maintenance-bucket item (dormant items never reach the timeline) } // ComputeDaySection sets the DaySection, IsOverdue, and IsAllDay based on the item's time diff --git a/internal/models/types.go b/internal/models/types.go index e3164de..8eda33b 100644 --- a/internal/models/types.go +++ b/internal/models/types.go @@ -28,6 +28,39 @@ type Task struct { RecurrenceWeekdays []int `json:"recurrence_weekdays,omitempty"` RecurrenceSeriesID string `json:"recurrence_series_id,omitempty"` NextOccurrenceOverride *time.Time `json:"next_occurrence_override,omitempty"` + + // Chain membership (doot-native tasks only). ChainID != "" is the + // indicator that a task belongs to a linear task chain. + ChainID string `json:"chain_id,omitempty"` + ChainPosition int `json:"chain_position,omitempty"` + ChainUnlocked bool `json:"chain_unlocked,omitempty"` + + // Maintenance bucket membership (doot-native tasks only). BucketID != "" + // is the indicator that a task is part of a bucket's pool. + BucketID string `json:"bucket_id,omitempty"` + BucketState string `json:"bucket_state,omitempty"` // "dormant" | "active" + BucketLastActiveAt *time.Time `json:"bucket_last_active_at,omitempty"` +} + +// Chain is a fixed, ordered sequence of native tasks with a WIP limit of +// exactly 1 -- only one task in the chain is ever unlocked/actionable. +type Chain struct { + ID string `json:"id"` + ProjectID string `json:"project_id"` + Status string `json:"status"` + CreatedAt time.Time `json:"created_at"` +} + +// MaintenanceBucket is a finite pool of native tasks that get repeatedly +// activated/deactivated: every CycleDays, PickN dormant items are selected +// and flipped active with a computed due date. +type MaintenanceBucket struct { + ID string `json:"id"` + Name string `json:"name"` + CycleDays int `json:"cycle_days"` + PickN int `json:"pick_n"` + LastCycleAt *time.Time `json:"last_cycle_at,omitempty"` + CreatedAt time.Time `json:"created_at"` } // Project is a lightweight grouping for doot-native tasks. Exactly one diff --git a/internal/models/widget.go b/internal/models/widget.go index bf148fd..1ac082e 100644 --- a/internal/models/widget.go +++ b/internal/models/widget.go @@ -19,6 +19,9 @@ type WidgetItem struct { Completable bool `json:"completable"` // true = doot task (checkbox shown) RecurringEventID string `json:"recurring_event_id,omitempty"` ProjectColor *string `json:"project_color,omitempty"` + ChainPosition int `json:"chain_position,omitempty"` // 1-indexed; 0 = not a chain task + ChainTotal int `json:"chain_total,omitempty"` + BucketState string `json:"bucket_state,omitempty"` // "active" when this is a maintenance-bucket item } // WidgetResponse is the full /api/widget response body. |
