| Age | Commit message (Collapse) | Author |
|
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
|
|
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VTUSAEKfsPc6WGDq45yPHD
|
|
Editable title/description (Edit -> Cancel/Save toggle), linkified
description, and independently-tappable date/recurrence/next-date
chips. Non-doot sources (Trello, Google Tasks) are unaffected --
same title + Complete button as before, no live fetch, no edit UI.
|
|
|
|
|
|
HandleWidgetTaskUpdate now returns 404 instead of silently succeeding
when the widget's cached task id no longer exists, matching every
other native_tasks mutator's checkRowsAffected pattern.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VTUSAEKfsPc6WGDq45yPHD
|
|
GET /api/widget/task, POST /api/widget/task/update,
POST /api/widget/task/recurrence, POST /api/widget/task/next-date.
Doot-only; the recurrence/next-date fields are null in the detail
response for a non-recurring task.
|
|
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.
|
|
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.
|
|
Implement pure function to compute next occurrence of recurring tasks given
frequency, interval, and optional weekday constraints. Supports daily, weekly,
monthly, and yearly recurrence patterns. Weekly recurrence with weekday
constraints wraps to next applicable week when needed.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VTUSAEKfsPc6WGDq45yPHD
|
|
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
|
|
Nine tasks: recurrence schema/model, ComputeNextOccurrence, iteration
creation on completion, periodic due-date trigger, HTTP endpoints,
Android WidgetRepository additions, description linkification,
TaskDetailActivity redesign, build/deploy.
|
|
29-31 anchors
|
|
Renders docs/superpowers/{specs,plans}/*.md to HTML via goldmark,
served from doot's own web server behind the existing session auth
instead of as raw files. DOCS_DIR is configurable (defaults to
docs/superpowers relative to the working directory) so the deployed
server can point straight at the working repo and stay live-synced
with no separate copy/deploy step for new docs.
|
|
New recurring-series model for doot-native tasks (server-owned, not
consumer-driven): new rows are created either on completion or once a
due date passes, whichever comes first. Redesigns the widget's task
detail popup with editable title/description, linkified URLs/phone
numbers, and independently-tappable date/recurrence/next-date chips.
|
|
legibility, soften overdue red
- AllDayRow now has the same 32dp leading gutter as HourRow's hour-label
column, so its color bar lines up with EventBlock's bar in the grid
below instead of sitting flush left; also dropped the title's
defaultWeight() so the multi-day label sits right after the title
instead of being pushed to the far-right edge.
- TaskFragmentBlock no longer renders its own "TODAY" sub-header --
it's always inside the already-labeled TODAY section, and Tomorrow's
floating tasks never had this redundant label to begin with.
- Section headers and hour/time labels bumped from ~30-40% to ~50-60%
opacity -- against a home-screen wallpaper the previous values were
barely legible.
- Overdue task text alpha reduced to 0.75 -- full-opacity bright red
read as too alarming.
|
|
|
|
The label was concatenated into the title string, inheriting its 0.9
alpha. Split into its own Text at full opacity, matching EventBlock's
treatment of upcoming (non-past) events.
|
|
|
|
Multi-day events (Start and End on different calendar days) are pulled
out of the normal grid/all-day pipeline and rendered as an all-day-style
row on every day they touch (Today and/or Tomorrow), labeled
starts/ends/plain per the day being rendered. Previously such an event
either only appeared in the single hourly grid slot matching its start
time (never again on later days) or, if genuinely flagged all-day,
never had its End forwarded at all.
|
|
|
|
A single-day all-day event's End.Date is one calendar day past its own
day (Google Calendar convention, already documented by
TestParseEventTime_AllDayEvent). isMultiDayEvent/multiDayVariant didn't
adjust for this, so every all-day event -- including single-day ones --
was misclassified as multi-day and duplicated across Today and
Tomorrow.
|
|
MD-Task 2's review caught that Google Calendar's all-day End.Date is
exclusive (a single-day all-day event's End is one day past its own
day), which isMultiDayEvent/multiDayVariant didn't account for. Task 4
(Android, not yet implemented) had the same flaw copied into its
Kotlin code -- fixed here before dispatching, mirroring the Go-side fix.
|
|
TimelineItemView wraps a TimelineItem with a per-render-day
MultiDayVariant (starts/ends/spans/none). HandleTimeline's bucketing now
places a multi-day event into both TodayItems and TomorrowItems when it
touches both, instead of only the list matching its start day.
|
|
A genuine all-day Google Calendar event spanning multiple days (e.g. a
3-day conference) never got its EndTime forwarded to the widget client,
since the conversion only set End when !IsAllDay. The client has no way
to detect a multi-day span without both Start and End.
|
|
|
|
Tapping a calendar event (or any event-type item) now opens the event's
URL (Google Calendar, Plan to Eat, etc.) directly instead of showing an
intermediate popup with an "Open in Calendar" button. Removes
EventDetailActivity and the recurrence-schedule lookup it was the only
consumer of: WidgetRepository.getRecurrence, the Go
/api/widget/recurrence endpoint, HandleWidgetRecurrence,
GoogleCalendarAPI.GetRecurrenceRule, and formatRecurrence, plus their
tests. RecurringEventID itself stays -- it's general calendar-sync
metadata used elsewhere in the timeline pipeline, not exclusive to the
removed popup.
|