<feed xmlns='http://www.w3.org/2005/Atom'>
<title>doot.git/migrations, branch master</title>
<subtitle>doot — personal productivity web app
</subtitle>
<id>https://git.terst.org/doot.git/atom?h=master</id>
<link rel='self' href='https://git.terst.org/doot.git/atom?h=master'/>
<link rel='alternate' type='text/html' href='https://git.terst.org/doot.git/'/>
<updated>2026-08-16T00:03:51+00:00</updated>
<entry>
<title>Replace Google Tasks service-account auth with real OAuth</title>
<updated>2026-08-16T00:03:51+00:00</updated>
<author>
<name>Peter Stone</name>
<email>thepeterstone@gmail.com</email>
</author>
<published>2026-08-16T00:03:51+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/doot.git/commit/?id=3660486153a16760d2b980e546bbbd29408fb8d4'/>
<id>urn:sha1:3660486153a16760d2b980e546bbbd29408fb8d4</id>
<content type='text'>
Service-account auth structurally cannot see a regular user's personal
task lists (no equivalent of Calendar's per-item sharing model) --
confirmed via GetTaskLists returning exactly the service account's own
empty "My Tasks" list, never the real user's three lists. Zero rows
were ever cached in production as a result.

Adds a standard 3-legged OAuth flow: /settings/google-tasks/connect
redirects to Google's consent screen (AccessTypeOffline+ApprovalForce
so a refresh_token is always issued), /callback exchanges the code and
persists the token (new oauth_tokens table), /disconnect clears it.
GoogleTasksClient now takes an option.ClientOption instead of a
credentials file path; NewGoogleTasksOAuthClient wraps it with a
dbTokenSource that reloads/refreshes from the DB on each access-token
expiry and re-persists -- carefully preserving the original
refresh_token when Google's refresh response omits one (it usually
does), which would otherwise silently and permanently break future
refreshes.

Settings page shows connection status and a Connect/Disconnect
button. Calendar keeps using service-account auth (that one actually
works). Requires a one-time manual step: create an OAuth 2.0 Client ID
in Google Cloud Console and set GOOGLE_OAUTH_CLIENT_ID/SECRET in .env
-- documented in .env.example.
</content>
</entry>
<entry>
<title>Remove the feature toggle system (dead code)</title>
<updated>2026-08-06T18:19:27+00:00</updated>
<author>
<name>Peter Stone</name>
<email>thepeterstone@gmail.com</email>
</author>
<published>2026-08-06T18:19:27+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/doot.git/commit/?id=06450fe69ade2928deb9274bb67b7ba60d394b4f'/>
<id>urn:sha1:06450fe69ade2928deb9274bb67b7ba60d394b4f</id>
<content type='text'>
Audited it (couldn't query the live DB directly -- auto-mode classifier
blocks direct production reads without prior approval -- so this is a
code-only audit): GetFeatureToggles/SetFeatureEnabled/IsFeatureEnabled/
CreateFeatureToggle/DeleteFeatureToggle had exactly one caller each, all
inside their own CRUD handlers. Nothing anywhere else in the codebase
read a toggle's Enabled state to gate any actual behavior -- confirmed
by grepping every remaining .Enabled/IsFeatureEnabled reference back to
either this dead code or its own tests. It was pure UI-managed CRUD with
no consumer, unlike Trusted Agents (wired into agent.go/websocket.go)
or Data Sources (wired into the sync pipeline) which stayed.

Removes the Settings page section, the three /settings/features* routes
and handlers, the five Store methods, the FeatureToggle model, and adds
028_drop_feature_toggles.sql (next free migration number, per this
repo's convention of never renumbering -- see 021_drop_tasks.sql for
the same drop-table-forward pattern) to drop the now-unused table.
Also removed the now-dead tests for all of the above.

go build ./... and go test ./... both clean.

Co-Authored-By: Claude Sonnet 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01EZ7ikw2ukGJFTHE3bJS7zL
</content>
</entry>
<entry>
<title>Implement linear task chains and recurring maintenance buckets</title>
<updated>2026-07-17T22:22:37+00:00</updated>
<author>
<name>Peter Stone</name>
<email>thepeterstone@gmail.com</email>
</author>
<published>2026-07-17T22:22:37+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/doot.git/commit/?id=b007fee8fb5b39a5f9b369c59af71ac9e795ceaf'/>
<id>urn:sha1:b007fee8fb5b39a5f9b369c59af71ac9e795ceaf</id>
<content type='text'>
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 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01EZ7ikw2ukGJFTHE3bJS7zL
</content>
</entry>
<entry>
<title>Add schema for task budgets and availability blocks</title>
<updated>2026-07-16T06:08:23+00:00</updated>
<author>
<name>Peter Stone</name>
<email>thepeterstone@gmail.com</email>
</author>
<published>2026-07-16T06:08:23+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/doot.git/commit/?id=310d17b25d8dbcd8e930148334e7bd7bb144f407'/>
<id>urn:sha1:310d17b25d8dbcd8e930148334e7bd7bb144f407</id>
<content type='text'>
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 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01PQaPGQVSfmKUiHXB87qRTC
</content>
</entry>
<entry>
<title>feat(tasks): add projects/labels schema, wire project_id through native tasks</title>
<updated>2026-07-16T02:54:17+00:00</updated>
<author>
<name>Peter Stone</name>
<email>thepeterstone@gmail.com</email>
</author>
<published>2026-07-15T18:06:03+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/doot.git/commit/?id=54e2f162c03ff7c732751ef35c266093c842c7bc'/>
<id>urn:sha1:54e2f162c03ff7c732751ef35c266093c842c7bc</id>
<content type='text'>
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.
</content>
</entry>
<entry>
<title>feat(tasks): add recurrence columns and model fields for native tasks</title>
<updated>2026-07-16T02:44:02+00:00</updated>
<author>
<name>Peter Stone</name>
<email>thepeterstone@gmail.com</email>
</author>
<published>2026-07-14T20:11:31+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/doot.git/commit/?id=0baf1134ea2d415aa55079ff98476afd0acf5811'/>
<id>urn:sha1:0baf1134ea2d415aa55079ff98476afd0acf5811</id>
<content type='text'>
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 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01VTUSAEKfsPc6WGDq45yPHD
</content>
</entry>
<entry>
<title>Merge github/master: reconcile with parallel widget work</title>
<updated>2026-07-13T07:11:04+00:00</updated>
<author>
<name>Peter Stone</name>
<email>thepeterstone@gmail.com</email>
</author>
<published>2026-07-13T07:11:04+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/doot.git/commit/?id=44abf42ed45aa8f285e7ce031cbb9ef1ade667ea'/>
<id>urn:sha1:44abf42ed45aa8f285e7ce031cbb9ef1ade667ea</id>
<content type='text'>
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 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01EZ7ikw2ukGJFTHE3bJS7zL
</content>
</entry>
<entry>
<title>feat(widget): editable task details, scrollable list, and overdue-task fixes</title>
<updated>2026-07-13T06:54:13+00:00</updated>
<author>
<name>Peter Stone</name>
<email>thepeterstone@gmail.com</email>
</author>
<published>2026-07-13T06:54:13+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/doot.git/commit/?id=8310f802dd9fc6ef5dff0be7f640f79c5b39987f'/>
<id>urn:sha1:8310f802dd9fc6ef5dff0be7f640f79c5b39987f</id>
<content type='text'>
- 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 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01EZ7ikw2ukGJFTHE3bJS7zL
</content>
</entry>
<entry>
<title>feat(widget): capture and forward RecurringEventID for calendar events</title>
<updated>2026-07-12T11:06:32+00:00</updated>
<author>
<name>Peter Stone</name>
<email>thepeterstone@gmail.com</email>
</author>
<published>2026-07-12T11:06:32+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/doot.git/commit/?id=f31b4722243b8b69564c94e47e07678153c31815'/>
<id>urn:sha1:f31b4722243b8b69564c94e47e07678153c31815</id>
<content type='text'>
</content>
</entry>
<entry>
<title>feat: remove Todoist integration entirely</title>
<updated>2026-07-06T00:00:59+00:00</updated>
<author>
<name>Doot Agent</name>
<email>agent@doot.local</email>
</author>
<published>2026-07-06T00:00:59+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/doot.git/commit/?id=945c34590677e161a711ccaff0e1077197b1b178'/>
<id>urn:sha1:945c34590677e161a711ccaff0e1077197b1b178</id>
<content type='text'>
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 &lt;noreply@anthropic.com&gt;
</content>
</entry>
</feed>
