| Age | Commit message (Collapse) | Author |
|
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
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PQaPGQVSfmKUiHXB87qRTC
|
|
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.
|
|
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
|
|
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
|
|
flag
|
|
|
|
|
|
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
|
|
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
|
|
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
|
|
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
|
|
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VTUSAEKfsPc6WGDq45yPHD
|
|
|
|
|
|
|
|
Tests verify ProjectColor is populated in BuildTimeline and copied to WidgetItem.
|
|
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.
|
|
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
|
|
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
|
|
|
|
|
|
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.
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
dialog)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VTUSAEKfsPc6WGDq45yPHD
|
|
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
|
|
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
|
|
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VTUSAEKfsPc6WGDq45yPHD
|
|
onEnabled only fires when the first widget instance is added to a home
screen, so if the periodic RefreshWorker job is ever silently dropped
(an app reinstall can clear WorkManager's persisted schedule without
the widget itself being removed/re-added -- exactly what happens when
sideloading a new APK build, as opposed to a Play Store update), there
was no way for it to come back except manually removing and re-adding
the widget. onUpdate fires far more often (reboot, periodic OS ticks)
and now also re-arms the schedule (a no-op via KEEP if already running).
Root-caused via manual refresh restoring all missing content instantly
(rules out a data/rendering bug) plus the widget only showing one
stale morning event beforehand (consistent with the periodic job
having stopped ticking hours earlier, right around today's APK
reinstalls).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VTUSAEKfsPc6WGDq45yPHD
|
|
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VTUSAEKfsPc6WGDq45yPHD
|
|
Task.IsRecurring/Atom.IsRecurring were never assigned anywhere in the
codebase (confirmed via full-repo search), so PartitionAtomsByTime's
"hide future recurring tasks until due" branch was permanently dead.
RecurrenceSeriesID is the real recurring indicator now; wiring up
"hide future recurring successors" as an actual feature is a separate
design decision, not part of this cleanup pass.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VTUSAEKfsPc6WGDq45yPHD
|
|
- SourceMeal/TypeMeal (AtomSource/AtomType): leftover from the removed
MealToAtom conversion (b2d8fc4); zero references anywhere else.
- HandleGetSourceOptions: never registered as a route, and its own
test comment already noted "may fail if template not found, which
is acceptable" -- the settings-source-options template it rendered
doesn't exist in web/templates/ at all. Fully unreachable/broken,
not just unused.
go build/vet/test all clean, no behavior change for anything reachable.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VTUSAEKfsPc6WGDq45yPHD
|
|
Two independent pre-existing failures, both from incomplete refactors:
- agent.go's four task write/create switches (complete/uncomplete,
update due date, update details, create) never got a "doot" case
added when the Todoist integration was removed (945c345) in favor
of native tasks -- the test file was updated to use source=doot,
but the handlers themselves still only recognized "trello"/"gtasks",
so every native-task Agent API call 400'd with "Unknown source".
getAtomDetails already had a "doot" case, confirming this was an
incomplete migration, not an intentional gap.
- TestMealToAtom in atom_test.go tested MealToAtom, a function removed
from atom.go months earlier (b2d8fc4) when meals were dropped from
the unified Atom/timeline system; the Meal struct itself is still
used elsewhere (shopping/meals feature) but no longer produces atoms,
and the test was never cleaned up to match.
go test ./internal/... ./cmd/... is now fully green with no failures.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VTUSAEKfsPc6WGDq45yPHD
|
|
Overdue and non-overdue tasks now render with the same plain title
color; no more red highlighting for overdue items.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VTUSAEKfsPc6WGDq45yPHD
|
|
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
|