summaryrefslogtreecommitdiff
AgeCommit message (Collapse)Author
2026-08-04Widget quick-add: surface errors instead of failing silentlyPeter Stone
addTask()'s failure path had no .onFailure handler, so a failed POST (network hiccup, server down) left the Add sheet just sitting there with zero feedback -- looked like the button did nothing. Also: the five calls chained after a successful addTask (updateTask, reschedule, setTaskProject, setTaskLabels, setTaskRecurrence) were still unchecked, so a task could get created with silently-dropped metadata even with the addTask fix in place. Both paths now collect and toast what failed; the task itself still gets created and the sheet still closes, since aborting would leave a task that already exists server-side with no way to tell the user without a duplicate.
2026-08-04Fix production wedge: propagate context to Google Calendar API callsPeter Stone
Three .Do() calls in google_calendar.go accepted a ctx parameter but never chained .Context(ctx) into the actual SDK call, so the existing global 60s request timeout never reached the blocking network call. One hung Google Calendar request wedged every DB/session-touching request path in production for three days (2026-08-01 through 2026-08-04), undetected because /health unconditionally returned 200 throughout. - Wire .Context(ctx) into GetUpcomingEvents, GetEventsByDateRange, and GetCalendarList. - Bound aggregateData's four external fetches with a per-fetch sub-context as defense-in-depth (only effective if the callee actually honors ctx -- documented as such, not oversold). - Make GetUpcomingEvents/GetEventsByDateRange fetch calendars concurrently instead of sequentially: a review of this fix caught that a shared per-fetch deadline over a sequential loop would starve calendars past the first under any real latency, silently caching partial results as complete. Concurrent fetches give every calendar an equal shot at the same deadline instead. - /health now does a real PingContext DB check instead of a static "ok" (Handler.PingDB, tested for both healthy and closed-DB cases). - Add internal/api/context_audit_test.go: an AST-based structural guard that fails any future .Do() call in google_*.go missing .Context(...) anywhere in its chain, so this class of bug can't silently recur. Verified by deliberately reintroducing the original bug against a backup and confirming the guard catches it. - Add scripts/health-watchdog.sh: cron job restarts the service if /health fails twice in a row, five minutes apart. go test ./... -race is green. Deployed and live-verified.
2026-07-28fix: Google Tasks due dates shifted a day earlier in negative-offset timezonesPeter Stone
Google Tasks' due field is date-only but always serialized as an RFC3339 timestamp fixed at midnight UTC regardless of the user's timezone. Converting that instant into displayTZ (dueDate.In(tz)) reinterpreted it as a real moment in time instead of re-anchoring the same calendar date to local midnight -- midnight UTC becomes 2pm the previous day in UTC-10 (Pacific/Honolulu), so every dated Google Task silently landed one day earlier than its actual due date. A task due tomorrow showed up in today's section. Fix: extract the Y/M/D from the UTC-anchored timestamp (which IS the intended calendar date) and rebuild midnight in displayTZ from those components, instead of converting the instant.
2026-07-25fix: undated tasks were landing in the widget's scheduled-events gridPeter Stone
ComputeDaySection unconditionally recomputed IsAllDay from a midnight-time heuristic, clobbering the IsAllDay:true flag callers set to mark floating (no-due-date) tasks -- since undated tasks use Time = now, this silently reset IsAllDay to false and gave them a real Start, so the widget could render them via EventBlock/HourRow instead of TaskRow. Their click handler always opens the calendar/source URL, so tapping a no-due-date task (e.g. a Google Task) opened Google Calendar instead of the task detail sheet. ComputeDaySection now only ever sets IsAllDay true, never clears an already-true value. Also flag IsAllDay for undated Google Tasks, which never got it set at all (only native undated doot tasks did).
2026-07-18fix(widget): wrap TomorrowSection in a Column to stop rows overlappingPeter Stone
TomorrowSection emitted its divider, header, and every event/task row as top-level siblings with no shared layout container. Since the whole function is composed inside a single LazyColumn item{} slot (one RemoteViews node), nothing told Glance to stack them vertically -- they all rendered on top of each other instead of flowing top-to-bottom. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ7ikw2ukGJFTHE3bJS7zL
2026-07-18Add bucket CRUD and read-only Projects/Labels to Settings pagePeter Stone
New "Maintenance Buckets" section: create a bucket, add a pool item by title (creates the task and assigns it in one step), remove an item, delete a bucket (unbuckets its tasks rather than deleting them). New read-only Projects and Labels sections (name + color swatch) -- both are simple enough that read-only is the right call on web, per user direction, rather than duplicating the Android popup's editing UX. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ7ikw2ukGJFTHE3bJS7zL
2026-07-18Rework Tasks tab: Chains section, checklist modal, project visibilityPeter Stone
The flat Tasks-tab atom list was silently dumping every chain step (locked and unlocked) and dormant bucket-pool items in as ordinary undated cards, with no chain/project context and no protection against completing a locked step out of order. - CompleteNativeTask now rejects completing a locked chain task (ErrChainTaskLocked), mapped to 400 in both the widget and web complete-atom handlers. - Chain tasks and dormant bucket items are excluded from the flat atom list; a new "Chains" section shows one card per active/paused chain with the current step and N/M progress. - New chain checklist modal (GET /chains/{id}) lists every position in order with pause/resume/abandon -- the web view originally deferred as Android-only. - Fixed a real bug this surfaced: resuming a paused chain only flipped the status flag, never unlocking the deferred successor, so a chain paused right after a completion stayed stuck forever. SetChainStatus now catches up the deferred advancement on resume, idempotently. - Atom cards gained a project-name chip for general visibility. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ7ikw2ukGJFTHE3bJS7zL
2026-07-17Extend chain creation to accept per-task description and priorityPeter Stone
CreateChain and POST /api/widget/chains previously only took bare title strings, with priority hardcoded to 1 -- narrower than the spec's "an ordered list of task titles/descriptions" API line. Now accepts []models.ChainTaskInput{Content, Description, Priority} per position, priority defaulting to 1 when omitted. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ7ikw2ukGJFTHE3bJS7zL
2026-07-17Document deferred UI screens for chains and bucketsPeter Stone
Fleshes out the "not built" notes in both specs into actionable follow-up scope: what data/API layer already exists, what the screen needs to do, and precedent to follow. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ7ikw2ukGJFTHE3bJS7zL
2026-07-17Guard deploy script to only run on tungstenPeter Stone
Refuses to run if hostname doesn't match the documented deploy host, preventing an accidental deploy from a sandbox/dev environment. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ7ikw2ukGJFTHE3bJS7zL
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-17chore(deploy): remove abandoned push-to-deploy scaffoldingPeter Stone
deployment/deploy duplicated scripts/deploy (the one actually in use). deployment/post-receive documented installing a git hook at /site/doot.terst.org/app-code/hooks/post-receive, but that hook was never actually installed and the app-code checkout it depended on has been removed -- this path was never wired up.
2026-07-17fix(widget): autofocus quick-add title field, redesign as blank edit formPeter Stone
Quick Add's text field never requested focus or triggered the keyboard, so tapping it opened a sheet with no visible way to type. Added a FocusRequester + delayed requestFocus()/keyboard show (ModalBottomSheet needs a beat to finish its enter animation before focus requests land). While in there, rebuilt the quick-add sheet to match the task edit popup instead of being a bare title field: due date, recurrence, project, and label chips, plus a description field, reusing the same dialogs the edit popup already uses. POST /api/widget/add now returns the created task's id so the client can chain the same project/labels/recurrence/due-date setter calls edit already relies on.
2026-07-16Add implementation plan for task budgets and availabilityPeter Stone
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PQaPGQVSfmKUiHXB87qRTC
2026-07-16Show scheduled/available badge on widget TODAY headerPeter Stone
Threads the /api/widget budget_status field through the Android widget's DataStore-backed cache (a new BUDGET_STATUS_JSON pref, since the widget decomposes WidgetResponse into individual prefs rather than caching it whole) and renders a small "Nm/Nm" badge next to TODAY when there's a nonzero tracked load.
2026-07-16Show a scheduled-vs-available indicator on the web timeline's Today sectionPeter Stone
Task 11 of the task-budgets-and-availability plan: reuse the computeBudgetStatus helper (Task 8) in HandleTimeline so the dashboard's Today section header shows scheduled/available minutes when a budget-tracked task exists. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PQaPGQVSfmKUiHXB87qRTC
2026-07-16Suggest an estimate from same-project/label averages on task detailPeter Stone
2026-07-16Add availability CRUD, task estimate, and budget-tracked toggle endpointsPeter Stone
Adds 6 widget HTTP handlers (availability get/create/delete, task estimate, project and label budget-tracked toggles) plus route registration, following the existing widget handler conventions. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PQaPGQVSfmKUiHXB87qRTC
2026-07-16Surface budget_status on GET /api/widget when tracked tasks existPeter Stone
Wires ComputeBudgetPeriod together with availability blocks, tracked project/label sets, calendar events, and native tasks into a new computeBudgetStatus helper on *Handler. HandleWidgetGet now populates WidgetResponse.BudgetStatus with today/week periods, but only when at least one incomplete budget-tracked task exists in the rolling week window -- otherwise the field is omitted entirely so unconfigured users see no new UI. Errors are logged and swallowed, matching the existing resilience pattern for this widget endpoint. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PQaPGQVSfmKUiHXB87qRTC
2026-07-16Fix overlapping-event double-subtraction in ComputeBudgetPeriod; clarify ↵Peter Stone
task-filter doc comment; add multi-day test Overlapping calendar events within the same availability block were each subtracted independently, double-counting their intersection and making availability under-counted (a 120-min block with two 60-min-overlap events that overlap each other 18:30-19:00 came out to 0 min instead of the correct 30 min). Replace the per-event overlapMinutes subtraction with busyMinutesInBlock, which clips each event to the block, merges the clipped intervals, and subtracts the union's total length. Also clarify ComputeBudgetPeriod's doc comment: task filtering only enforces an upper bound (due < end); start is intentionally not used to filter tasks, since callers are expected to pre-fetch and pass in already-overdue tasks. Add a multi-day-window test verifying availability sums correctly across days, a block only contributes on its matching weekday, and the day-iteration loop excludes the day at `end`. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PQaPGQVSfmKUiHXB87qRTC
2026-07-16Add pure availability-minus-events and scheduled-load computationPeter Stone
ComputeBudgetPeriod is a standalone, dependency-free function over in-memory availability blocks, calendar events, and tasks: sums weekly availability minus overlapping calendar events for [start, end), and sums estimated minutes for tracked, incomplete tasks due in that window.
2026-07-16Infer a default estimate from same-project/label averagesPeter Stone
2026-07-16Add budget-tracked toggles for projects/labels; fix SetLabelColor wiping the ↵Peter Stone
flag
2026-07-16Add availability_blocks CRUD to the store layerPeter Stone
2026-07-16Add estimated_minutes to native task read/write/recurrence pathsPeter Stone
2026-07-16gofmt internal/models/types.go and budget.goPeter Stone
Fix formatting and alignment violations: align struct field tags in Task, realign Weekday comment in AvailabilityBlock, and remove extra blank line before CalendarEvent. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session
2026-07-16Add budget/availability model typesPeter Stone
Add EstimatedMinutes to Task, BudgetTracked to Project and LabelColor structs. Create budget.go with AvailabilityBlock, BudgetPeriod, and BudgetStatus types for modeling task budgets and calendar availability. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PQaPGQVSfmKUiHXB87qRTC
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-16fix(agent): pass project_id through on Agent API task creationPeter Stone
HandleAgentTaskCreate decoded project_id from the request but never forwarded it to CreateNativeTask, so a task created via the external Agent API with a project assigned would silently create the task unassigned instead. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VTUSAEKfsPc6WGDq45yPHD
2026-07-16Update worklog for task labels and projects featurePeter Stone
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VTUSAEKfsPc6WGDq45yPHD
2026-07-16feat(widget): show project color as a small accent on task rowsPeter Stone
2026-07-16feat(widget): add project picker and label editor to task detail popupPeter Stone
2026-07-16feat(widget): add Android data layer for projects and label colorsPeter Stone
2026-07-16test(tasks): add tests for project color threadingPeter Stone
Tests verify ProjectColor is populated in BuildTimeline and copied to WidgetItem.
2026-07-16feat(tasks): thread project color from BuildTimeline to WidgetItemPeter Stone
Projects are fetched once per BuildTimeline call into a color lookup map, not queried per-task -- same pattern as RecurringEventID's existing TimelineItem->WidgetItem threading.
2026-07-16test(tasks): add test cases for project and label endpointsPeter Stone
Tests for HandleWidgetProjectsGet, HandleWidgetProjectsCreate, HandleWidgetTaskProject, HandleWidgetTaskLabels, HandleWidgetLabelsGet, HandleWidgetLabelsColorSet, and the extended HandleWidgetTaskDetail response with project and labels fields. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VTUSAEKfsPc6WGDq45yPHD
2026-07-16feat(tasks): add HTTP endpoints for projects and label colorsPeter Stone
Extends the task-detail response with project/labels so the popup can display them without a second round trip. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VTUSAEKfsPc6WGDq45yPHD
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-16Add implementation plan for task labels and projectsPeter Stone
Nine tasks: schema/model + project_id wiring, Projects CRUD, label-color CRUD, HTTP endpoints, TimelineItem/WidgetItem project-color threading, Android data layer, task-detail popup UI (project picker + label editor), widget grid color accent, build/deploy.
2026-07-16Add design docs for four backlog task-scheduling ideasPeter Stone
Labels/projects, budgets/availability, recurring maintenance buckets, and linear task chains -- refined via a 24-question interview (2026-07-15) into concrete data models, mechanisms, and API shapes. None approved for implementation yet; each spec notes its dependencies on the others (labels/projects is foundational to two of the other three). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VTUSAEKfsPc6WGDq45yPHD
2026-07-16refactor(widget): split DootWidget.kt by responsibilityPeter Stone
DootWidget.kt (582 lines) mixed the GlanceAppWidget entry point/root composable, all the row-rendering composables, and multi-day-event detection logic in one file. Split into: - MultiDayEvents.kt: MultiDayVariant, effectiveEndDay, isMultiDayEvent, multiDayVariant, multiDayLabel, timeSuffix. - WidgetRows.kt: sourceColor, calendarViewIntent, and all row/section composables (AllDayRow, RefreshButton, QuickAddButton, HourRow, EventBlock, TaskFragmentBlock, TaskRow, TomorrowSection, TomorrowEventRow), plus hourLabel/calcGridStart/calcGridEnd. - DootWidget.kt: just the GlanceAppWidget class and WidgetRoot, now 145 lines. All same package (org.terst.doot.widget.ui), so no import changes needed for cross-file references. Pure move, no behavior change -- full unit test suite (including DootWidgetGridTest/ DootWidgetMultiDayTest, which call the internal functions directly) passes unchanged. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VTUSAEKfsPc6WGDq45yPHD
2026-07-16refactor(store): extract agent session/trust methods from sqlite.goPeter Stone
Pure move: all agent-session and agent CRUD/trust methods (~340 lines) plus their full test suite and setupTestStoreWithAgents helper, out of sqlite.go/sqlite_test.go into agents.go/agents_test.go. No behavior change. Mirrors internal/handlers/agent.go's domain naming. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VTUSAEKfsPc6WGDq45yPHD
2026-07-16refactor(store): extract calendar and Google Tasks methods from sqlite.goPeter Stone
Pure move: SaveCalendarEvents/GetCalendarEvents/GetCalendarEventsByDateRange into calendar.go, SaveGoogleTasks/GetGoogleTasks/GetGoogleTasksByDateRange into google_tasks.go. Neither cluster had dedicated tests in sqlite_test.go to move. No behavior change. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VTUSAEKfsPc6WGDq45yPHD
2026-07-16refactor(store): extract shopping methods from sqlite.go into shopping.goPeter Stone
Pure move: UserShoppingItem type, its CRUD methods, and SetShoppingItemChecked/GetShoppingItemChecks, plus their tests and setupTestStoreWithShopping helper, out of sqlite.go. No behavior change. Mirrors the existing internal/handlers/shopping.go naming. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VTUSAEKfsPc6WGDq45yPHD
2026-07-16refactor(store): extract meal methods from sqlite.go into meals.goPeter Stone
Pure move: SaveMeals/GetMeals/GetMealsByDateRange and their test (plus its setupTestStoreWithMeals helper, used only by that test) out of the 1400+ line sqlite.go into their own file. No behavior change. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VTUSAEKfsPc6WGDq45yPHD
2026-07-16Update worklog for widget polish batch (fonts, instant complete, recurrence ↵Peter Stone
dialog) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VTUSAEKfsPc6WGDq45yPHD
2026-07-16fix(widget): redesign recurrence dialog layoutPeter Stone
The 4 frequency chips and 7 weekday chips were each laid out in a single non-wrapping Row -- on a real phone width these overflow/get cut off rather than wrapping. The interval field was a full-width OutlinedTextField with the unit baked into its label ("Every N dailys"/"weeklys" -- ungrammatical for anything but weekly), and the whole dialog had no visual grouping. Redesigned: FlowRow (wraps instead of overflowing) for both chip rows; single-letter weekday chips (S M T W T F S) to stay compact; a narrow fixed-width (72dp) interval field paired with a correctly-pluralized unit label rendered separately from the field; muted section labels (REPEATS / EVERY / ON THESE DAYS) for structure. No callback/API changes -- purely a layout rewrite of the same onSave/onClear/onDismiss contract. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VTUSAEKfsPc6WGDq45yPHD
2026-07-16fix(widget): match task font color to events; complete instantlyPeter Stone
TaskRow used a flat hardcoded gray (0xFFDDDDDD) while EventBlock used Color.White (dimmed only when past) at the same size/weight -- the color mismatch read as a font difference. Both now use the same Color.White base. CompleteTaskAction previously did no local update at all: the row stayed visible until CompleteWorker's full complete() -> fetchAndPersist() -> updateAll() round trip finished (two sequential network calls). It now optimistically removes the completed item from the cached list and re-renders immediately, matching RefreshTaskAction's existing synchronous-flag-then-updateAll pattern -- the background worker's own fetchAndPersist still replaces this with the authoritative server state (including a newly-created recurring successor, if any) moments later. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VTUSAEKfsPc6WGDq45yPHD