Skip to content

feat(protocol): thread/list, thread/fork and thread/archive - #231

Merged
oratis merged 1 commit into
mainfrom
feat/protocol-thread-list
Aug 3, 2026
Merged

feat(protocol): thread/list, thread/fork and thread/archive#231
oratis merged 1 commit into
mainfrom
feat/protocol-thread-list

Conversation

@oratis

@oratis oratis commented Aug 3, 2026

Copy link
Copy Markdown
Owner

Finding F15 in docs/THREE_WAY_REVIEW.md — the last open item from the review.

The protocol could start, read and resume a thread but not enumerate one. So every client that wanted a thread picker read the session directory itself: the desktop through a Tauri command, with its own row shape, its own ordering and its own idea of a title. Two readers of the same data.

The three methods

Method Behaviour
thread/list id, cwd, timestamps, title (first user message, truncated), turn count — newest first
thread/fork copies a thread into a new one, original untouched
thread/archive moves the snapshot out of the listing

Behind a threadManagement capability. ThreadStore gains optional list/archive: a store without them leaves the capability off and the runtime rejects the calls, rather than letting an empty list pass for the truth. listThreads() returns null to the desktop client in that case, so "the server can't tell me" and "there are no threads" stay distinguishable.

The canonical store lists snapshots first, then projects any legacy session that has no snapshot yet — a listing shows everything a user has, not just what the app-server has touched since 0.2.0. Legacy rows are projected lazily and not written back.

Fork copies an in-progress turn as interrupted. Nothing is executing the fork, and startTurn refuses a thread that already has an in-progress turn — so carrying the status across would produce a fork that could never be used. There's a test that forks mid-turn and then starts a turn on the result.

Wired, not just declared

The desktop sidebar lists through the protocol now, falling back to the Tauri reader only when the sidecar is too old. A capability nothing consumes is the failure mode this whole review batch has been about.

Verification

7 new runtime tests (ordering, title derivation, no title for an empty thread, archive removing a row, archiving a nonexistent thread, fork isolation, in-progress fork, and a store that can't do any of it). The preview fixture serves thread/list and thread/archive, so the existing Playwright journey — which picks sessions by title out of the sidebar — is now exercising the protocol-backed listing end to end.

pnpm typecheck · lint · format:check clean; protocol 31 · core 750/16 skipped · desktop 101 · server 41 · cli 208 · vscode 12 · lsp 13 · scripts 21; playwright test → 7 passed.

Follow-up: archive and delete in the sidebar still go through Tauri; thread/archive is served but not yet the path the button takes. That's the remaining half of removing the second reader.

🤖 Generated with Claude Code

Finding F15 in docs/THREE_WAY_REVIEW.md. The protocol could start, read and
resume a thread but not enumerate one, so every client that wanted a thread
picker read the session directory itself — the desktop through a Tauri command,
which is a second reader of the same data with its own idea of what a row is.

Adds the three methods behind a `threadManagement` capability. ThreadStore
gains optional list/archive; a store without them leaves the capability off and
the runtime rejects the calls rather than letting an empty list pass for the
truth. The canonical store lists snapshots first and then projects any legacy
session that has no snapshot yet, so a listing shows everything a user has, not
just what the app-server has touched since 0.2.0.

Fork copies a thread into a new one and leaves the original untouched. An
in-progress turn is copied as `interrupted`: nothing is executing the fork, and
carrying `in_progress` across would leave it permanently unable to start a turn.

The desktop sidebar now lists through the protocol, falling back to the Tauri
reader only when the sidecar is too old to serve it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@oratis
oratis merged commit 9977227 into main Aug 3, 2026
5 checks passed
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.

1 participant