summaryrefslogtreecommitdiff
path: root/internal/store/native_tasks_test.go
AgeCommit message (Collapse)Author
2026-09-03Make the day's list a pull of committed work, not a push of everything unblockedPeter Stone
due_date had been carrying two incompatible meanings: "this has a real deadline" (Bills, meds, a signup cutoff) and "this is when I intend to work on it." Chains leaned entirely on the second -- every step was stamped due_date = now the moment it unlocked -- so a five-minute call and a six-month certification both arrived looking overdue, and the only way to quiet them was to hand-reschedule. The list therefore always contained work that could not be finished that day, which made ending a day at zero impossible by construction rather than by effort. Splits the two apart: - due_date keeps only its real meaning: an externally-imposed deadline. - committed_date (new, migration 031) means "I chose this for today." Chain steps no longer get a due date on unlock (CreateChain, advanceChain, and the delete-promotion path), and are excluded from the undated list so they don't simply fall through to it instead. They reach the day only by being committed, via a "+ today" control on the chain card. Bucket-active items get the same control. BuildTimeline pulls in committed tasks, deduped against tasks already present for a different reason (committed and due-today are two reasons for one row, not two rows). Migration 031 also clears the auto-stamped due dates from open chain steps; completed steps keep theirs as historical record. Two supporting changes made while in here: - Extracted the native_tasks SELECT list into nativeTaskColumns. It had been copy-pasted into nine queries that must each agree exactly with scanNativeTasks' field order; adding committed_date meant editing all nine correctly or silently breaking whichever was missed. - Extracted templateFuncMap() and added TestTemplates_AllParse. New() only logs a warning when template parsing fails, so a malformed template shipped silently and surfaced as a broken page at runtime. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018AwnALZL6bD4VBtATDVv3B
2026-09-03Abandon recurring occurrences superseded by their next iterationPeter Stone
A weekly task not done before the next one lands is stale, not still owed. The scheduler already created the successor regardless of completion (deliberately, so skipping one doesn't stall the series), but nothing retired the occurrence it replaced -- so every skipped occurrence stayed open forever as a permanent overdue item. Live: "Bills" and "Bones's meds" each sat in the list twice. AdvanceDueRecurringTasks now marks the superseded occurrence abandoned when it creates the successor. A separate flag rather than `completed`, because these were never done and crediting them would corrupt completion history. All four open-task queries filter it out; migration 030 adds the column and backfills the occurrences already superseded (exactly the 2 above, verified against production before deploying). Supersedes the earlier view-level collapse (10f9fcd, reverted in 96518ad): that kept the *earliest* open instance, which is backwards -- the stale one should go and the current one stand -- and it only hid the row in the flat list while leaving it overdue in the timeline and widget, which is where it was actually being seen. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018AwnALZL6bD4VBtATDVv3B
2026-08-12Add task title editing/deletion, timeline click-to-open, widget app launchPeter Stone
Task-detail modal was description-only with no delete affordance; HandleUpdateTask now saves the title too and a Delete button hits a new DELETE /tasks/{id} route backed by store.DeleteNativeTask, which repairs chain_position/unlocks the successor when the deleted task belongs to a chain. Timeline tab task/card/gtask rows now open the same detail modal as the Tasks tab. Android widget's "TODAY" header is now a tap target that launches DashboardActivity, since nothing previously opened the full app from the widget.
2026-07-17Implement linear task chains and recurring maintenance bucketsPeter Stone
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
2026-07-16Add estimated_minutes to native task read/write/recurrence pathsPeter Stone
2026-07-16Add schema for task budgets and availability blocksPeter Stone
Creates migrations/025_task_budgets_and_availability.sql with: - New availability_blocks table for manual weekly availability templates - estimated_minutes column on native_tasks for time budgeting - budget_tracked flag on projects and labels for opt-in tracking Fixes pre-existing bug in sqlite_test.go's setupTestStoreWithNativeTasks where project_id column was missing from the schema, causing test failures. Updates all hand-rolled test helpers (sqlite_test.go, native_tasks_test.go) to include the new columns, and adds availability_test.go with newAvailabilityTestStore helper for Task 4's availability CRUD tests. All store tests pass, including the two that were previously failing (TestGetNativeTasksByDateRange_ExcludesOverdue, TestGetOverdueNativeTasks_IncludesOnlyPastDue). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PQaPGQVSfmKUiHXB87qRTC
2026-07-16feat(tasks): add label color CRUD store methodsPeter Stone
2026-07-16feat(tasks): add Projects CRUD store methodsPeter Stone
2026-07-16feat(tasks): add projects/labels schema, wire project_id through native tasksPeter Stone
project_id joins the existing labels JSON column as series-level metadata: CreateNextIteration copies both onto every new recurring occurrence, matching content/description/priority's existing behavior.
2026-07-16fix(tasks): make CreateNextIteration atomically self-guardingPeter Stone
Folds the "is this still the latest row in its series?" check directly into the INSERT as a single atomic INSERT...SELECT...WHERE NOT EXISTS statement, instead of a separate SELECT-then-INSERT sequence. Closes a narrow race where the completion trigger and the periodic due-date check could both pass their "still latest" check before either had inserted, producing two duplicate successor rows for the same series. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VTUSAEKfsPc6WGDq45yPHD
2026-07-16feat(tasks): add periodic due-date check for recurring tasksPeter Stone
A recurring task's successor now also gets created once its due date passes, independent of completion -- an ignored/overdue recurring task no longer blocks the next occurrence from appearing. Runs every 15 minutes via a new goroutine in main.go, cancelled on shutdown.
2026-07-16feat(tasks): create the next recurring iteration on completionPeter Stone
CompleteNativeTask now creates a new row for the next occurrence when completing the latest iteration of a recurring series (using the one-shot next_occurrence_override if set, else ComputeNextOccurrence). Completing an already-superseded row (the periodic due-check beat it to creating the successor) just marks it completed, no double-create.
2026-07-16feat(tasks): add recurrence columns and model fields for native tasksPeter Stone
recurrence_series_id != "" is the real recurring indicator (IsRecurring predates this feature and is never set). Adds parseWeekdays/formatWeekdays for the comma-separated weekday-list column, and threads the five new columns through scanNativeTasks and all four existing SELECT queries. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VTUSAEKfsPc6WGDq45yPHD
2026-07-12fix(widget): surface unmatched task IDs instead of silently no-oppingPeter Stone
CompleteNativeTask/UncompleteNativeTask/RescheduleNativeTask ran a plain UPDATE ... WHERE id = ? and returned whatever error Exec gave back -- which is nil even when 0 rows match, since that's not a SQL error. A stale or wrong id from the widget looked identical to a real completion: HTTP 200, nothing changed in the database. Real incident: 1 of 3 widget completeTask taps silently no-opped this way. Now checks RowsAffected() and returns ErrNativeTaskNotFound (mirrors the existing pattern in sqlite.go's ApproveAgentSession/DenyAgentSession). HandleWidgetComplete and HandleWidgetReschedule surface this as 404 instead of a fake 200, so the widget can tell 'nothing changed' apart from 'it worked.'