| Age | Commit message (Collapse) | Author |
|
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
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
Another session pushed 27 commits in parallel covering quick-add, event
detail popups, recurrence display, overdue badges, a manual refresh button,
and its own fix for the same overdue-tasks bug (via a separate
GetOverdueNativeTasks fetch folded into BuildTimeline, rather than widening
GetNativeTasksByDateRange's bound directly). Reconciled rather than blindly
taking one side:
- Reverted GetNativeTasksByDateRange to its original bounded query and kept
upstream's GetOverdueNativeTasks + BuildTimeline fold-in as the sole
overdue mechanism for native tasks, to avoid double-counting overdue
items (my widened query + their separate fetch would have both returned
them). Re-pointed the regression test at the now-correct contract and
added a store-level test for GetOverdueNativeTasks directly.
- Kept my GetGoogleTasksByDateRange fix as-is (single unbounded query) --
upstream never touched Google Tasks overdue handling, so there's no
duplication risk there.
- Rewove WidgetRoot's LazyColumn structure (added for scrolling) around
upstream's new header buttons, pinned all-day event rows, and the
enhanced TomorrowSection, none of which were written LazyColumn-aware
since that work landed on this side only.
- Combined both sides' additions to TaskDetailActivity/TaskDetailSheet
(description-edit detail popup + due-date reschedule label) and
WidgetRepository/Actions (optimistic local removal + refresh button
wiring) -- these were independent, non-overlapping features that both
needed to survive.
- Renumbered the migration collision: both sides independently added a
migration numbered 022. Card-description was already applied to the live
production DB under that filename earlier this session (migrations are
tracked by filename), so it keeps 022; the recurring-event-id migration,
never deployed under any name here, moves to 023.
Verified: go build clean, full test suite passes (only the two
pre-existing agent-handler failures and the pre-existing models package
build error remain, both confirmed unrelated via git stash before this
session began), and a dry run against a copy of the live production
database applies both migrations cleanly with no re-run conflicts.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EZ7ikw2ukGJFTHE3bJS7zL
|
|
- Add description editing to the widget's task detail popup for
doot/gtasks/trello, backed by new GET /api/widget/detail and
POST /api/widget/update endpoints
- Make Google Tasks and Trello cards completable via the widget (Trello
completion archives the card); fix Trello description never being
fetched, which meant saving could silently wipe a card's real desc
- Fix google_tasks.due_date/updated_at (TEXT columns) never round-tripping
through sql.NullTime, which broke cached Google Tasks reads whenever
the cache was valid
- Fix native-task and Google-Task date-range queries excluding anything
due before the window start, which dropped incomplete tasks off the
widget the moment their due day passed (the "overdue tasks disappeared"
bug)
- Fix native task description edits blanking the task's title
- Make the widget's day list scroll (LazyColumn) instead of clipping
- Optimistically remove a task from the widget immediately on completion,
ahead of the authoritative background refresh
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EZ7ikw2ukGJFTHE3bJS7zL
|
|
|
|
|
|
|
|
|
|
|
|
GetNativeTasksByDateRange's SQL bound (due_date >= start) excluded any
task overdue from a previous day before ComputeDaySection ever got a
chance to mark it IsOverdue, so both the web Timeline view and the
widget API (which both call BuildTimeline with start = today) silently
dropped overdue tasks entirely -- only the Tasks tab (unbounded
GetNativeTasks) showed them. Added GetOverdueNativeTasks and folded its
results into BuildTimeline alongside the ranged fetch.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VTUSAEKfsPc6WGDq45yPHD
|
|
fetchCalendarEvents falls back to config.GoogleCalendarID whenever no
source_configs rows exist yet for the gcal source -- which is the
current live state (0 rows). GoogleCalendarID is a single env var that
itself holds a comma-separated list of calendar IDs, but the whole
joined string was being wrapped in a single-element []string{...} and
passed straight to SetCalendarIDs. Google's API takes one calendarId per
call, so every fetch failed with '404 Not Found' on the literal
comma-joined string -- confirmed live in production logs. This broke all
calendar events, in both the web dashboard and the widget, silently.
Now splits on commas and trims whitespace before use.
|
|
WidgetItem.isAllDay was carried by the client's data model but nothing
ever read it. All-day events had no Start at all, so they fell into the
same floating-task queue as ordinary untimed tasks; if enough tasks were
ahead of one in the queue, SlotPacker could assign it an hour slot past
the visible grid range entirely -- not merely unpinned, actually
invisible.
Server: TimelineItemToWidgetItem now populates Start for all-day
CALENDAR EVENTS specifically (their real event date), while leaving
undated doot/gtask tasks -- also flagged IsAllDay as a "no specific
time" fallback, a different concept -- on the existing nil-Start
floating behavior.
Client: all-day events are filtered out of the hourly grid/floating-task
pipeline entirely, bucketed by day using the new Start date, and
rendered in a new pinned AllDayRow section right after the TODAY/TOMORROW
headers.
|
|
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.'
|
|
Native tasks (native_tasks table) fully replace Todoist. All Todoist
API code, store functions, handlers, routes, templates, and tests have
been removed. Migration 021 drops the now-unused tasks cache table.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Reconciles diverged histories: deploy/master had gateway proxy Host-header
and publicPaths fixes that were never synced back to local/github; this
branch had the native-task Tasks-tab/completion fix. Merging both.
|
|
Bug 1: BuildUnifiedAtomList never called GetNativeTasks(), so doot-sourced
tasks were absent from the web Tasks tab. Added GetNativeTasks() fetch and
NativeTaskToAtom conversion (new model helper, SourceDoot constant).
Bug 2: handleAtomToggle called getAtomDetails after CompleteNativeTask, but
GetNativeTasks filters WHERE completed=0, so the task was already gone and
the title came back blank. Moved getAtomDetails call to before the completion
switch so all sources (including doot) capture title/dueDate first.
Also fixed widget_test.go compile error: TestHandleWidgetComplete_NonTodoist
called HandleWidgetComplete as a package-level function but it is a method on
*Handler. Rewrote the file as package handlers (internal) and construct
&Handler{} directly.
Regression tests added for both bugs in atoms_test.go and handlers_test.go.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
DNS-rebinding check
mcp.NewStreamableHTTPHandler rejects requests where the server is
bound to loopback but the Host header is not (DNS-rebinding protection).
Requests proxied through doot arrived at claudomator with
Host: doot.terst.org, triggering a 403.
Set req.Host = target.Host in the ReverseProxy Director so upstream
services see the target host (e.g. 127.0.0.1:8484) rather than the
original public hostname.
|
|
- Calendar: fall back to GOOGLE_CALENDAR_ID config when no source_configs
synced yet (fixes blank calendar after fresh deploy)
- Meals: call fetchMeals in HandleTimeline so PlanToEat cache refreshes
on every timeline load, not just during manual refresh
- Tomorrow section: replace calendar-grid with flat chronological list
matching widget layout (time label | source bar | title)
- Add /health endpoint (no auth required) for deploy health checks
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Tapping a doot task shows a date picker. On confirm, POSTs to
/api/widget/reschedule, updates due_date in native_tasks, refreshes
widget. Reschedule button only shows for source="doot" tasks.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
TODOIST_API_KEY is no longer required — native task management works
without it. Guards nil todoistClient in handleAtomToggle.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Adds doot-owned task storage (native_tasks table) so tasks can be
managed without Todoist. CompleteTask for 'doot' source just updates
the DB — no external API call, no token dependency.
Migration path:
POST /settings/import-from-todoist — copies Todoist cache → native_tasks
Then remove TODOIST_TOKEN from .env to disable Todoist
Changes:
- migration 020: native_tasks table
- store: GetNativeTasks, GetNativeTasksByDateRange, GetUndatedNativeTasks,
CreateNativeTask, CompleteNativeTask, UncompleteNativeTask,
UpdateNativeTask, ImportFromTodoist
- timeline: native tasks appear as source="doot" (teal)
- handleAtomToggle: "doot" case — no external API needed
- HandleWidgetComplete: method on Handler, handles "doot" natively
- HandleUnifiedAdd: "doot" source creates in native_tasks
- widget: "doot" tasks are completable, teal color indicator
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Adds GET /task?id=xxx&source=xxx route that renders a full mobile-friendly
task detail page (session-protected). Widget task rows now open this page
when tapped. HandleUpdateTask redirects back to the page after a non-HTMX
save. Android: threads serverUrl through composable chain to TaskRow.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
|
|
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Implements WidgetAuthMiddleware (static bearer token), TimelineItemToWidgetItem
(conversion helper), HandleWidgetGet (today's items as JSON), and
HandleWidgetComplete (proxies task completion to Todoist).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|