Skip to content

The To-do destination: routed tasks land on a list the walker can work (#160) - #165

Merged
wschenk merged 4 commits into
mainfrom
cursor/todo-destination-160
Aug 8, 2026
Merged

The To-do destination: routed tasks land on a list the walker can work (#160)#165
wschenk merged 4 commits into
mainfrom
cursor/todo-destination-160

Conversation

@wschenk

@wschenk wschenk commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Summary

route = "todo" Threads get their real destination (docs/desk.md, slice D2):

  • The task list — a new /todo surface (linked from the Days header) lists every Thread routed to To-do as a checkable item in the walker's own words: the first Capture's text, never the Enrichment's title. It re-reads after each sync cycle, so a to-do routed on another device lands live.
  • Check-off is destination-surface state, not Filing — a new todoDoneAt field rides its own seam (POST /api/sync/todoThreadRepository.setThreadTodoDone) and deliberately never touches reviewedAt or route, so checking an item off cannot reopen or re-file the Thread. It persists in the local store (IndexedDB), round-trips through the memory and Neon repositories (todo_done_at column), and hydrates across devices in mergeRemoteThreads the way the review decision does.
  • The day digest checklist draws from routed to-dos instead of re-deriving. The day chat sends the day's routed to-dos with every ask; buildDayDigestPrompt carries them verbatim (- [ ] / - [x]), and DAY_DIGEST_SYSTEM_INSTRUCTION makes them the checklist when the section is present — deriving from reports only for callers that predate the field.
  • Un-routing removes the item by construction — the list is fed by route = "todo" itself, not a copy, so clearing or redirecting the route at the desk takes the item off the list. This composes with the Day flow's unfileThread (The default door: a Day with unrouted Threads opens on the arrival summary (#159) #168), which clears the route.

Closes #160

Merged with main (662a5cc)

This branch was last of the five-way fan-out, so it carries a merge of main at 84875ab (after #167 timeline, #166 journal, #169 spec routing, #168 day flow). Six files conflicted and every one was resolved by keeping both sides' additions — no sibling's work was dropped:

File Resolution
lib/local-capture/types.ts todoDoneAt + specHandoff on LocalThread and applyRemoteThreads
lib/sync/types.ts both fields on ServerThread; setThreadTodoDone + recordSpecHandoff both on the repository interface
lib/sync/hydrate.ts both fields at all three sites in mergeRemoteThreads
lib/sync/memory-repository.ts both on StoredThread and the listThreads mapping
lib/sync/neon-repository.ts both columns (separate ADD COLUMN migrations), both in the SELECT, the row type, and the mapping
app/globals.css append-vs-append; both style blocks kept in sequence

Note for whoever merges this file next: lib/sync/hydrate.ts contains a literal NUL byte in a join() separator, so git classifies it as binary, refuses to merge it, and keeps one side wholesale with no conflict markers. Trusting "no markers = resolved" here would silently drop the other side's field from the adopt-list while still compiling. This merge diffed both sides and reapplied the additions byte-wise, preserving the NULs. Replacing that separator with an ordinary string is worth a follow-up.

Test plan

  • Acceptance criteria from the ticket covered at the named seams:
    • tests/todo-destination.spec.ts (memory repositories / pure seams): routing to To-do lists the walker's words; check-off round-trips without re-filing; todoDoneAt hydrates both ways; un-routing and redirecting remove the item; the digest prompt and system instruction carry routed to-dos, with absence keeping the old derive behavior.
    • tests/todo-destination-ui.spec.ts (public browser seam, thread-filing-ui pattern): route via the desk filing UI → item appears on /todo in the walker's words → check-off sticks across reload → the day still reads "All filed" → un-routing empties the list; the day-digest ask POSTs routedTodos for that day.
  • Verified on the merged tree: pnpm exec tsc --noEmit clean; eslint clean on every changed file; 48 passed / 1 skipped / 0 failed across todo-destination, todo-destination-ui, thread-filing, thread-filing-ui, day-flow, sync-hydrate, sync-repository, day-digest, day-digest-ui. (The skip is the Clerk setup project, which skips without preview keys.)
  • Both neighbours' behavior confirmed alongside mine: all seven Day-flow tests pass (receipts and undo included), and routedTodos survives end to end.

#160)

route = "todo" Threads get their real surface (docs/desk.md, D2): /todo
lists them as checkable items in the walker's own words — the first
Capture's text, never the Enrichment's title. Checking one off is an
action on the destination (todoDoneAt on its own seam, /api/sync/todo);
it never reopens or re-files the Thread, and it survives reload and
hydrates across devices like the review decision does. Un-routing at
the desk removes the item by construction — the list is fed by the
route, not a copy.

The day digest checklist now draws from that day's routed to-dos
instead of re-deriving tasks: the client sends them with every ask, the
prompt carries them verbatim, and the system instruction makes them the
checklist when present.

Closes #160

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KPox3By2txjxSy8HUAPVKk
@vercel

vercel Bot commented Aug 8, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
walking-thoughts Ready Ready Preview Aug 8, 2026 9:27am

Request Review

Mechanical refresh from the skills sync tooling at session start:
updated vendored copies under .agents/skills and their computed hashes
in skills-lock.json. No product code touched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KPox3By2txjxSy8HUAPVKk
…on-160

# Conflicts:
#	app/globals.css
#	lib/local-capture/types.ts
#	lib/sync/hydrate.ts
#	lib/sync/memory-repository.ts
#	lib/sync/neon-repository.ts
#	lib/sync/types.ts
…on-160

# Conflicts:
#	components/desk-workspace.tsx
@wschenk
wschenk merged commit 71a819d into main Aug 8, 2026
2 of 3 checks passed
wschenk pushed a commit that referenced this pull request Aug 8, 2026
#165 landed, so the To-do row's "Landing next" pill is now false. It
reads Live, points at /todo, and says the thing worth knowing about
that surface — ticking an item off is the list's own business and
never re-files the Thread. The setup section drops the caveat it no
longer has.

The status pills are the one part of the legend that is hand-held
rather than derived, which is exactly why they need this pass every
time a destination ships.

No-ticket: true

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mm4zPLAX77USAqyk12jgc1
wschenk added a commit that referenced this pull request Aug 8, 2026
#165 landed, so the To-do row's "Landing next" pill was false. It reads Live, links /todo, and says the thing worth knowing about that surface — ticking an item off is the list's own business and never re-files the Thread. The setup section drops the caveat it no longer has.

The status pills are the only hand-held part of the legend; the rest derives from THREAD_ROUTES and routeForKind. That is why they need a pass whenever a destination ships. The one remaining hand-held claim is Spec's "Needs a token".

No-ticket: true
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

To-do destination: routed tasks land on a list the walker can work

2 participants