Repository navigation
Two computers: continue a moved chat, clean leader quit, removed-device wording - #1967
Merged
Merged
Conversation
A bare start that continued another computer's released chat dropped the command batch Home returned, which carried the directory ask whose single-flight guard it had already set. The guard then stayed set, so the window kept the listing it read before the take: the first message opened "Continue this chat here?" for a chat this computer held, and choosing continue here retired this computer's own session and released its own hold before the conditional take was refused. Home's commands are now returned on every automatic continuation outcome. A completed take marks the chat as held here at once and replaces a listing that was asked before it. A send decides the continuation card from a fresh ownership read rather than the cached listing, and that read no longer ends an outstanding listing request. Continuing checks the fresh holder before retiring anything: a chat this computer already holds stays open as it is, and a different holder is refused before local work is touched. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… coordination back A personal computer that led coordination ran its member inside the open terminal and stopped it when the terminal exited. The engine's closing release for the chat it held then reached a coordinator that was already leaving, got 503, and was never tried again, so the chat read as held offline until its lease ran out and coordination paused until the anchors elected a new leader. A coordinating terminal now waits, as it leaves its chats, for each idle chat's closing release to finish, then transfers leadership to an anchor and waits for an anchor to answer a fresh majority read before its member stops. That hand-back is bounded by LeaderQuitWithin (5 seconds); if the anchors cannot take over, the ordinary fallback applies. The anchors then restore their own voters as fallback already does, and the computer that quit does not get leadership back. A terminal that is not coordinating leaves exactly as before. A closing release that meets a temporary coordination refusal, including a bare 503, is retried within its existing close deadline; a definitive refusal is never retried. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… lead The cluster control client read every answer other than 200, 404 and 409 as a coordination outage, including the 401 a removed device receives. So on a removed computer `codeaf cluster status` said the cluster could not coordinate and told the person to reconnect voting computers, while `codeaf devices` correctly said the device was removed. A 401 carrying the directory's revoked code is now the removed-device error, and `codeaf cluster status` and `codeaf cluster lead` explain it with the same sentence `codeaf devices` uses, from one shared definition. Every other refusal keeps its words. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…hats, and keep version checks side-effect free Four small findings from a two-computer run: - Choosing continue here on the card a send raised replaced the displaced session with a fresh one and lost the words held behind the card. The arriving chat's box now receives the same draft, pasted text and attachments, unsent until the person presses enter again. - Taking a chat back into a displaced window counted the retired session's closing save of unchanged bookkeeping as unsent turns, so the move said edits were kept and Home grew a branch row for turns the relay already had. A sealed turn is now kept as a branch only when the engine finds real local work in it, the same rule continuing already uses. - Every window registers its empty new chat with the directory, and the other computer listed it as an untitled running chat. A record with no saved turn is no longer drawn on another computer's Home; it appears once its first turn is saved. Registration and ownership are unchanged. - codeaf --version, help and manual requests ran the former-folder adoption and removed old scheduler timers before parsing anything, so a version check could move a state folder and recreate the compatibility link. Those informational requests now skip both; every real start keeps them. The footer's fit-by-priority, the engine outliving its window, and /quit closing the conversation in front are the documented behaviour and are unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The entry is numbered when the pull request exists. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…nswer them The new paragraphs about a send's ownership check and the words kept after continue here lengthened the page's first section until "continue on another computer" no longer reached the section that says where moving works, and the clean-quit heading competed for the same words. They now have a section of their own and the clean-quit heading says what it is about. The version-request note on the help page drew "old version of the file" away from the task pages; the starting page already says that version, help and manual requests change nothing. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
AbirAbbas
marked this pull request as draft
October 26, 2026 16:47
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Taking a chat back into a window another computer had displaced reported "your unsaved edits here were kept as 1 turn" and left a branch row on both Homes, in both directions. A real reply appends pace, usage and call accounting after its shared save, and retiring the displaced session seals that tail. Both the take's unsaved-work check and the old drive's closing publish counted it as new work. Local work is now decided from the journal beyond the shared boundary: accounting records are not work, while conversation turns, tool results, unreadable records and workspace changes are still kept. The same decision guards the unsaved-work check and the closing publish, so neither creates a branch for what the relay already has. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A send in a displaced window raises Home with the continuation card. The typed words then live in the conversation's stashed composer, not in the input Home shows, and retiring the displaced session can clear them before the moved chat opens. The moved chat opened with an empty box. The conversation's composer is now taken when the confirmed take starts, carried with the take's answer and restored into the arrived chat, even when its transcript or session differs from the displaced one. Nothing is sent until the person presses enter. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A window's launch chat saves a bookkeeping head at start, and that head was published, so the other computer listed the never-used chat as "untitled running on <device>". A directory record always has a head, so the earlier filter on an empty head could never match. The drive keeps saving startup bookkeeping locally but notes a head for publication only once its saved journal holds a conversation message, bounded to that save, so text typed later cannot publish an earlier empty snapshot. A never-used chat therefore never reaches another computer's Home; it appears with its first turn and keeps ownership, quit and continue as before. A keyless end-to-end run covers the displaced take-back in both directions and both Homes. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
`codeaf cluster status` and `codeaf cluster lead` start with a control request whose handler answers an authentication refusal as a plain 403, so the client still reported the generic coordination failure on a removed computer. When a cluster request fails without a typed cause, the command now checks through the signed directory read whether this device was removed and, if so, explains it with the `codeaf devices` sentence. An unexplained 403 is never guessed to be a removal, and other failures keep their words. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
After a removal, the open window kept listing chats, sending presence and heartbeats and running state and member rounds every few seconds, each answered 401. The sync HTTP gate now remembers the removed refusal, and so do directory watches and a committed local removal. A marker bound to this device, identity and relay shares that fact across windows and restarts, and blocked requests are answered locally with the same removed refusal. State and member rounds stand down and open members close. Pairing installs a new device key, which lifts the marker, and an open Home resumes with the new device. Local chats keep working throughout. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Home's repository band ran `git status` without `--no-optional-locks`, so each refresh could create `.git/index.lock` and rewrite `.git/index`. When that landed during a move's in-place restore, the restore saw `.git` change and cancelled, and the take gave the chat back. Every git command codeaf runs on its own to read a repository now passes `--no-optional-locks`, so a background read never takes the index lock or rewrites the index. Commands that deliberately write are unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
After a failed take the chat was free, so the window's send door, which only blocked a chat another computer held, let a turn run on a stale copy that could never publish. A send now goes ahead only when this window holds the chat at the fence it recorded, or the chat was never shared. A free chat whose local head equals the shared head is taken back quietly with a conditional take, then sent; otherwise the continuation card or the view-only state appears and the words stay in the box. A failed take marks the window's copy as one it may not send on and says why in plain words, and a stale-fence refusal makes the window view-only. When the ownership check cannot reach the other computers, a chat this window holds keeps working and its turns wait to be shared, and anything else stays unsent. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The draft captured when a take started was restored only on success, so a lost race, a refused or unreachable take, or a failed in-place restore lost the words typed behind the card. Every failure path now puts the draft back in the box, unsent, and the manual says what a failed move leaves behind. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A removed computer's Home still offered "Continue <device>'s latest chat here?" with its chord. Once this computer knows it was removed, Home drops the left-off offer, the continue chord and the continue action on rows, and keeps the removal sentence. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The device pages keep to the words people use for their computers; the removal paragraph now says the computer stops asking the other computers for chats, and that pairing can still reach the service. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The test drove the drive's heartbeat every 10 ms from the start, which queued relay commits ahead of the member's first catch-up. On a slow disk the member round waited minutes, and the request signed before that wait was refused as clock skew. The heartbeat now keeps an ordinary pace until the removal is learned and only then runs fast, so the observation still spans many heartbeat intervals. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
After every take the window that took the chat said "<its own device> continued this chat; this window now only shows it". The take's acquire is followed by the arrived drive's own acquire, which raises the fence, and the window compared the listing's fence with the hold it recorded at the take. The window's recorded hold now follows its own drive's publishing receipt, so a newer fence held by this computer for this window is still this window's hold. The notice and the card come only from a hold by another computer; a stale local receipt still cannot send. The second acquire stays: it is the boundary that keeps two local drives from publishing under one fence. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A take captured whatever the window's main box held and restored it into the chat that arrived, so an unsent draft typed for another chat appeared in the moved chat's box and was saved as its draft. The arrived chat's box now holds only the words typed for it: the draft held behind the card its own send raised, otherwise its own saved draft, otherwise nothing. A draft typed for another chat stays with that chat, including when a take fails. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ip to takes Keeping each chat's draft with that chat had also stopped an ordinary opening from adopting the draft a dead window left in its directory, which is how the words follow the person and the directory. Adoption is restored. Only a chat arriving through a take now brings just its own saved words and never adopts another chat's draft. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The removal and empty-chat sections spoke of "another computer" and "adding the computer again", which drew the question away from the section with the pairing steps once that section's card text changed. They now say "machine" and "your other devices", and the question reaches the steps again. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
AbirAbbas
marked this pull request as ready for review
October 26, 2026 19:46
AbirAbbas
added a commit
that referenced
this pull request
Oct 26, 2026
…urrow v0.3.4 Brings the workspace-ownership branch up to dev cf282dd, which adds the Raft relay (#1950), the two-computer fixes (#1967), #1972, #1974 and #1976. - directory record types are now clusterstate aliases, so frame hints move into the replicated state machine: FrameHint and the frame_hints field on Cell, CellInit and Publish, bounded deterministically in the shared rules, dropped from listings and kept on single-cell reads. Signed v1 publishes with hints are accepted; unknown fields are still refused; existing canonical bytes are unchanged and no schema or protocol version moves. - every take path records the send lease, including the workspace-set and identity-adopting restores; an already-held chat keeps its hold when the record fails. - a removed computer cannot take through the folder-mapping road either, and unsent words and the pending send survive the root chooser. - the seal refuses a folder holding home before taking the checkpoint lock. - furrow pin follows dev at v0.3.4, which contains v0.3.2's non-mutating SQLite capture. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Member
|
@AbirAbbas heads-up: this PR looks like it broke 8 Bisected on Spark (
Failing, all 30 s timeouts:
Repro: (Run |
This was referenced Oct 28, 2026
Open
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.
A real two-computer test of dev after #1950, run on a Windows PC under WSL and a Mac against a self-hosted relay with a real model, found the bugs below. This PR fixes all of them. Moving a chat between computers now works with no workarounds, and both machines confirmed it over five rounds.
Fixes
codeaftook the chat, the window's chat listing never refreshed, because the startup batch dropped the answer to an ask whose single-flight guard was already set. The first send then asked "Continue this chat here?". Choosing continue here released the computer's own hold, and the window showed "another device continued this chat first". Now:--no-optional-locks. Home's repository read had created.git/index.lockduring a take's in-place restore, and the restore cancelled.codeaf cluster statusandcodeaf cluster leadas fromcodeaf devices. It stops all sync traffic until it is paired again; before, it polled about every 2–5 s and got 401s. It also no longer offers to continue chats.codeaf --versionandcodeaf helpchange nothing on disk.Checks
cluster lead: under 1 s;🤖 Generated with Claude Code