From b007fee8fb5b39a5f9b369c59af71ac9e795ceaf Mon Sep 17 00:00:00 2001 From: Peter Stone Date: Fri, 17 Jul 2026 22:22:37 +0000 Subject: 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 Claude-Session: https://claude.ai/code/session_01EZ7ikw2ukGJFTHE3bJS7zL --- internal/handlers/handlers.go | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'internal/handlers/handlers.go') diff --git a/internal/handlers/handlers.go b/internal/handlers/handlers.go index e427e40..343d0b1 100644 --- a/internal/handlers/handlers.go +++ b/internal/handlers/handlers.go @@ -488,6 +488,31 @@ func (h *Handler) HandleCompleteCard(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) } +// HandleDeferAtom returns an active maintenance-bucket item to its pool +// without crediting it as done -- distinct from complete/uncomplete. +// Doot-native tasks only (bucket items don't exist for other sources). +// No special swap needed: a successful defer removes the task's due date, +// so the same timeline refresh that follows completion just makes it +// disappear from the current view like any other now-undated task would. +func (h *Handler) HandleDeferAtom(w http.ResponseWriter, r *http.Request) { + if err := r.ParseForm(); err != nil { + JSONError(w, http.StatusBadRequest, "Failed to parse form", err) + return + } + id := r.FormValue("id") + if id == "" { + JSONError(w, http.StatusBadRequest, "Missing id", nil) + return + } + if err := h.store.DeferNativeTask(id); err != nil { + JSONError(w, http.StatusInternalServerError, "Failed to defer task", err) + return + } + w.Header().Set("HX-Reswap", "none") + w.Header().Set("HX-Trigger", "refresh-tasks") + w.WriteHeader(http.StatusOK) +} + // HandleCompleteAtom handles completion of a unified task (Atom) func (h *Handler) HandleCompleteAtom(w http.ResponseWriter, r *http.Request) { h.handleAtomToggle(w, r, true) -- cgit v1.2.3