Repository navigation
Conversation
session.llm.text.delta and session.llm.thinking.delta gain `offset`, the part's text length (JS string length) before the delta, so a client can splice a delta onto its draft or notice a gap. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The execution lifecycle events and retry.scheduled are durable, and the
session projector writes the thread's run state (run_state, its times,
outcome and error) from each event's own timestamp. At startup, before the
server listens, the harness ends every run a dead process left open with
execution.interrupted (crash, or shutdown when the run was closed); a run
recovery resumes says so with execution.started { resumed: true }.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A single-row store table holds the database's id and its last revision. Project, space, session, thread, entry and inbox rows carry the rev of the write transaction that last touched them, enforced by insert and update triggers. Store.write runs a write transaction (or joins the enclosing one), Store.rev takes its revision on first use, and a commit that took one rings a store-wide doorbell (Store.wake). Every write path goes through it: event projectors, plain-SQL thread, session, project and space writes. Part writes stamp their entry, document writes stamp the threads they configure, and deleting a session leaves a removed row. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A run the loop ends itself (crashloop, interrupted, truncated) publishes session.run.failed and returns normally, so the coordinator also published session.execution.succeeded and the thread row lost the failure reason. Runner.run and abandon now return an outcome; the execution's settled hook publishes no terminal after a run that already ended itself. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ssion and thread rows Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Entries carry their size, the bytes of their parts as stored, computed in SQL. Thread.linePage reads the newest page of a thread's line within a byte budget and a row count, past an optional seq. Sync.open reads a thread's first page and pending inbox; Sync.thread reads its own entries and inbox rows by revision. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A thread's newest page within a budget and its pending inbox on first open, or its rows changed since a revision on resume, then the same live after each commit, with the thread's text and thinking deltas merged in. HistoryEntry carries size. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…table app.db gets the desktop sync's schema: machine (replacing servers), organization (seeded "personal"), grouping, project, space, session, thread, entry, part and inbox, with foreign keys so removing a machine cascades to everything copied from it. The migrations collapse into one that creates the schema directly and seeds the built-in local machine. servers.* read and write the machine table and accept remote machines: a server is local or remote, and a remote one is added and updated with its WebSocket URL. Only a local server can be the default or run codework service. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
server.info gains storeId and rev; index.sync, thread.sync and thread.page join the mirror with the project, space, session, thread summary, run and live delta shapes they carry. HistoryEntry gains size, so the session widget's stand-in entries carry one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A Sync service keeps app.db a copy of each machine's CodeWork over its own connection: index.sync from machine.cursor, a full sync for a new or restored store, and thread.sync for opened threads from thread.cursor. Each batch applies in one transaction with deferred foreign keys; new threads get their first-seen status, priority and ordering, and run changes move status with a sync/status event in the same transaction. Removed sessions are soft-deleted with their detail kept. Live deltas that continue a draft are forwarded on the app bus, never stored. sync.open, sync.close and sync.page join the app contract, and the Event service gains transact so rows and their bus events commit together. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rder and color App RPCs set a thread's status, priority, color, group and order, a session's color, and create, update, move and delete groups. Each write lands with its bus event in one transaction: status edits on sync/status, the rest on sync/tracking with a tagged payload. Moves write one row at the midpoint of the neighbours, or the lowest minus one / highest plus one. Out-of-range values and missing rows are typed TrackingErrors. The E2E helpers move out of the sync suite into a shared fixture. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…back Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Runs the CodeWork fixture server (scripted model) on real OrbStack Linux machines, syncs them all into apps/server on the Mac and misbehaves the network: first sync under concurrent writes, resume after an iptables partition, deletes while away, crash sweep and recovery, restored and replaced stores, a netem-throttled live reply, a fork over a busy source, one repository on two machines with removal cascade, and revision ordering with a refused write. The machine set grows from 3 to 5 and shrinks again; each case and the shuffle leave an artifact under __artifacts__. The machine scripts (setup, start, stop, net, drive) live in test/machines; the fixture server gains a bind host, long replies and a sweep-only mode. The suite is skipped where orb is not installed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The machine's boot (orb start until its server answers from the Mac) ran from under a second to three minutes and pushed the step's bucket past its edge, so the shuffle artifact drifted. A step can now time its sync apart from what it waits on: the artifact buckets that sync time, the printed timings and the measured file keep both. The wait for the rebooted machine is raised to 8 minutes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The app server serves two reads over app.db alone: `sidebar.list` (an organization's active projects with their sessions and main threads, on the default machine or every machine) and `threads.line` (one keyset page of a thread's line, inherited entries included, with parts, the pending inbox and an untitled fork's ancestor title). `readTopics` names the bus topics each read refreshes from. The sidebar and switcher read run state from the synced rows; the working set, `projects.list` and its `thread.list` pass-through are gone. The session backend opens a thread's sync on follow, pages from app.db (fetching below its floor with `sync.page`), forwards the sync's notices and deltas on the thread's topic, and sends commands to the machine the session is on. It no longer keeps kv snapshots or subscribes to CodeWork's global event feed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…st delta, end a follow on removal The switcher reads the run state of the thread each frame shows. The view starts a part from a delta at offset 0 and holds deltas for a reply whose row has not landed, merging them once it does. A follow waiting for a thread ends as soon as its session is removed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dashboard.list splits an organization's threads by status, priority or group, each list in ordering and every list present, with the groups in their ordering; a thread's color falls back to its session's. Clients refresh it from sync/changed, sync/status and sync/tracking. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The dashboard lists threads by status, priority or group, with controls for each, group management, and drag to reorder or move between lists. A prompt not yet in app.db shows pending and a stop not yet idle shows stopping, drawn after 200 ms and held 300 ms, in the session view, the workspace tab and the sidebar row. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A thread missing from app.db is waited for only until its machine's index reaches the store head read at the follow (a row in flight, such as a session just created); still missing then, it is not found, as thread.info reported it. A deleted session or thread fails at once. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… off the dashboard A session's place in the sidebar is the newest change to any of its threads, forks included, not its main thread's alone. Subagent threads are not listed on the dashboard. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…y display reads idle The switcher reads each thread's run_state rather than a running flag, so a starting run reads as starting. A stop no longer clears when the first display reads the run idle: the session view hears it before the sidebar list refreshes, so the tab drew the run again until its own copy caught up. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e list Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A committed message offers a fork. The session backend forks the thread on the session's machine, waits for the machine's index to bring the new thread into app.db, and answers with its id; the view then opens the fork in a new frame. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Desktop sync:
apps/serverkeeps its own copy of CodeWork (.notes/SYNC.md). The harness becomes a versioned store, the CodeWork RPC streams rows by revision,apps/servermirrors every machine intoapp.db, and the web app reads the mirror.Harness. Store identity and a store-wide revision on project, space, session, thread, entry and inbox, enforced by triggers; session tombstones; a doorbell after each commit (
Store.write/rev/head/wake). Durableexecution.*andretry.scheduledevents projected ontothread.run_*, with a startup sweep before the server listens; an abandoned run ends withsession.run.failedonly. Text and thinking deltas carryoffset.CodeWork RPC.
server.inforeturnsstoreIdandrev. New streamsindex.sync({ after })andthread.sync({ sessionId, threadId, after?, budget })send rows withrev > afterthenSynced, then the same live;thread.pagereads older pages of a thread's line.HistoryEntry.size,QueuedPrompt.admittedSeq, extendedSessionInfo/ThreadInfo,origin.cut.apps/server. The
app.dbschema of SYNC.md section 5 (machinereplacesservers;organization,grouping, copiedproject,space,session,thread,entry,part,inboxwith desktop columns). ASyncservice per machine: full sync on a new or restored store, otherwise resume from cursors; batches applied with deferred foreign keys; soft deletes; opened-thread detail withsync.open/sync.close/sync.page; deltas folded by offset; bus events in the same transaction. User-side tracking: status, priority, groups, ordering, color. Reads overapp.dbonly:sidebar.list,threads.line,dashboard.list.apps/web. Sidebar and session widget read the synced tables; the
workingset, kv snapshots andprojects.listare gone; commands route to the session's machine; fork from a message; a Linear-like dashboard with drag reordering;pending/stoppingdisplay states.Verification
packages/harness/test/__artifacts__/,packages/codework/test/__artifacts__/andapps/server/test/__artifacts__/(store revisions, run lifecycle across a kill and restart, delta offsets, the two streams and paging, the schema, sync cases, tracking, reads, dashboard).apps/server/test/sync.machines.live.test.ts: ten cross-machine cases and a 3-to-5 machine shuffle on real OrbStack Linux machines with partitions (iptables), throttling (tc netem), crashes and restored or replaced stores; skipped withoutorb.app.dbat launch, streaming replies, a second client following, forks, restart mid-reply, the dashboard and the display states.vp checkandvp run typecheckpass; harness 617 tests, codework andapps/serversuites green.packages/codework/test/recovery.test.tshas a pre-existing socket-open flake on restarts, present oncanary.Notes
app.dbmust be deleted; harness databases from before this change are not upgraded.session.create/thread.fork, reconnect jitter, a bounded app bus, purging soft-deleted sessions, tombstones for stray project and space deletes, a second Electron window.🤖 Generated with Claude Code