Repository navigation
Merge app/v3 into workstreams/main (fourth catch-up) - #2338
Merged
Merged
Conversation
#2285) * pairing: a failure says its sentence once, and a finished pairing drops its check number - B2: the failure sentence is the pairing's error only; it is no longer also appended as a status line, so the desktop sheet shows it once. - B3a: finish() clears Words with the code and link, so no check number stays on screen after pairing succeeds, fails or is cancelled. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * sync: log why a chat could not be shared, once per chat and reason (T7) The sharing pass kept only that a chat failed and showed one sentence; nothing recorded the cause. Each failure now logs the chat and its error the first time that reason appears, so retries do not flood the log. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * tui: an approval that timed out says it may still have arrived (T2) "no answer from your devices — try again" blamed devices that take no part and invited a retry the service would refuse. A timed-out answer now says the sync service did not answer and to check /devices first; a timed-out lookup says the sync service did not answer. The manual quotes both. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * pair: the terminal approval keys sit on their own line (T9) `codeaf pair approve` printed the check number and "a / d" on one line, so the typed answer ran into the question. The keys now follow on their own line as "a approve · d decline:", matching the chat card. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * manual: correct how a removed computer rejoins and where older chats are prepared (D1) - A removed computer comes back with `codeaf pair --replace`; plain `codeaf pair` does not bring it back. - Older chats are prepared in the background only by the terminal chat, not by the desktop app. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * app: a removed computer reads the removal and "codeaf pair --replace", not "wireauth: device revoked" (B11) - Engine errors that are the revoked-device refusal reach the app as chatlist.Removed instead of the wire sentinel's text, so the chats area no longer shows "wireauth: device revoked". - Removed now says "run codeaf pair --replace to bring it back": plain `codeaf pair` does not bring a removed computer back (B1). Notices stored with the earlier wording are still recognised as a removal. - The manual quotes the new sentence. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * sync: a failed continue logs its reason, and the app test pins the take failure's Detail (B5a) - The "continue <id> phases … outcome=failed" line now ends with the error as reason="…", so chat.log names why a move failed. - appTakeError already carries the cause in Detail; the S5 take-failure test now checks it reaches the app over the wire and is not the generic sentence. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * pairing: the code instruction says dashes are optional (F4) The engine already reads a join code with or without dashes and spaces (pair.ReadJoinCode, covered by TestReadCodeShapes). The instruction now says so: "and type 42-715-302 (dashes optional)". Copy pins and the desktop-sync manual quote follow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * manual: restore the fresh-computer --replace exception, scoped so it cannot contradict rejoining (D1 follow-up) A computer whose chats hold only startup headers still pairs without --replace when it first joins (identity.Joinable). A removed computer already holds the identity, so pair.adopt stops at "already joined" before that check and only --replace brings it back. The sentence now says both. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * pairing: a removed computer pairs back from the app (B1) A removed computer keeps the identity and its revoked device key, so pairing it again ended "already paired; nothing changed" and left it removed. Only --replace minted a new key, and the app never sends it. - pair.Joining gains Revoked. When it is set and the grant is the same identity, adopt renews the device key as Replace does. With the same identity nothing local is given up; a different set of devices still follows the normal rules. - The app's use_code and request_link ask the directory whether this computer's own device is refused (ownDeviceRevoked) and set Revoked. - New TestB1AppRePairsARemovedComputer covers code and link re-pairing after removal (new key, same identity, chat kept, old key still removed, restart requested) and an unremoved computer still being told nothing changed. - The manual says the app brings a removed computer back on its own. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * tui: the approval timeout says "sync", not "the sync service" (T2 follow-up) The word law (#1508) keeps "service" out of person-facing text, and TestNoPersonFacingStringInThisSurfaceSaysLane caught approve.go. Both timeout sentences now begin "sync did not answer in time", as the other sync sentences do, and the manual quotes them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * approve: an approval whose reply was lost still pairs, and its link stops asking (T1, T3) - After an approve times out or is refused as already answered, the door reads the request again. Approved for this device and listed in this computer's own fleet counts as success: the solo marker ends and the card closes as approved. - Still waiting means nothing arrived: the screen says the plain retry. Unknown keeps the "may still have arrived" sentence. - Reopening the link of an approval that landed says the device is already paired and ends solo, instead of asking to approve again. - The device record gets its own 8 s wait, and an answer's overall wait is 20 s, so a slow first write cannot spend the approve's time. - Tests: a slow relay that approves but loses the reply (both computers end up paired), one that never hears the approval (the retry pairs), and a reopened link after a lost approval. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * devices: a roster read's own last-seen stamp no longer publishes devices.changed (B8b) A hosted relay stamps the asking computer's last seen on every request, so each roster read moved this computer's stamp and devices.changed went out. The app answers that event with another read, and the two kept each other going while a window was open: constant relay traffic, and the Computers page reloading every few seconds. publish now judges a change with this computer's last seen, and that of any computer online now, left out; neither is shown. The event still carries the current stamps, and an offline computer's new last seen or a change in who is online still publishes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * changes: entry for the pairing QA fixes Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * tui: a declined answer that timed out is the plain retry, not "check /devices" (T2 follow-up) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * app: a removed computer's refusal names no chat and points at Pair a computer (B11 follow-up) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * manual: rejoining keeps chats syncing and memories and settings in place (D1 follow-up) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * changes: the lost-reply fix is the terminal screen's, and the app's removal remedy Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * changes: re-adding a removed computer is not finished (B1) Two-computer testing on 2026-11-03 still showed neither computer listing the other after re-pairing a removed one; the entry no longer says it comes back. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* test(factory): expose hold lost with heartbeat reply Keep a red regression at the real HTTP Hold/heartbeat/worker seam. Drop one heartbeat reply only after the factory drains its hold. The 30-second check has already started, so job completion cannot explain the failure. The runner remains unpaused after 249 successful subsequent heartbeats. Leave production code and TestAPauseAndResumeReachTheRunner unchanged. The lease forgets Hold at lease/signals.go:115 before serve/workers.go:523 writes its reply. A transport failure gives worker/worker.go:538 no signal, so Runner.Hold/setPaused and the paused log are never reached. Subsequent heartbeats have no hold to redeliver. Repeating Hold(true) is also a no-op because lease/doors.go:276 already sees the factory's paused flag. Validation: the new regression fails with GOMAXPROCS=1 and -race. The original -race -count=200 loop reproduced four missing-pause failures and one separate resume failure. A CPU-loaded throwaway characterization observed 81 consecutive lost holds, all still running without a paused log. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(factory): capture pause worker and factory reports Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(factory): require heartbeat hold redelivery and resume precedence Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(factory): redeliver wanted hold on every heartbeat Read the current pause intent on every live drain. Resume queues a fresh lease and suppresses an older uncollected hold; ordinary drains and the wire vocabulary remain compatible. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(factory): expose pause log lost when cancelled drive finishes Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(factory): retain worker release snapshots for pause diagnosis Under CPU load the natural pause test failed eight times in sixty runs: the hold arrived, reports carried only the check log, and Done successfully released the lease. The factory remained paused and resumable despite the raw running state. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(factory): save pause before the drive can cancel its context The held drive exits through settleSpend, which cancels its context. Publishing c.paused before entering move allowed that exit to reject the pause log write. Change the control within the admitted store write so the log survives and the worker flushes it before releasing its lease. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(factory): format pause regressions and document live hold delivery Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(factory): bound concurrent hold and spending answer regression Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(factory): schedule spending answer before item-locked hold Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(factory): use runner clock for lock scheduling Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(factory): require atomic saved holds at step boundaries Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(factory): persist holds outside the loop lock before cancelling Serialize pause transitions with round admission. Store callbacks never acquire the loop lock, so spending answers cannot deadlock with holds. Failed writes leave the control and execution unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(factory): hold only the first save in the step boundary regression Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(factory): capture the first hold retention hook caller Instrument the existing failing boundary test before changing the fixture. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(factory): block the hold save after its preliminary read The first-hook goroutine stack showed Hold at loop.go:2149 calling Store.Get and retainOutput at store.go:219, before setPaused acquires pauseMu. Arm the fixture through the runner clock inside setPaused so the blocker intercepts the pause write. Preserve every boundary, persistence, cancellation and failed-save assertion; remove temporary stack logging. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: record the hold boundary fixture correction Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: keep the boundary fixture note with the existing hold change The changelog gate rejects PR zero in the existing hold entry. Fold this related fixture note into that entry instead of introducing a second rejected fragment; no pull request number has been assigned. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: changelog keeps to what a person sees Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * changelog: name the pull request Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ws say a take is running (#2314) * pairing: the app and codeaf pair approve settle a lost approval reply too The terminal chat screen's T1/T3 fix now covers the desktop app's approve_link and codeaf pair approve: an approve that timed out or was refused as already answered is looked up with settleApproval, so a device the relay let in counts and the approver ends solo; a reopened link this computer already approved says already paired and ends solo instead of failing as already answered. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * app: chat rows say when a take of that chat is running Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * changes: one entry for the pairing follow-ups; #2285's entry keeps what ships there Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * changes: number the pairing follow-ups entry Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * app: a finished take never says taking:false on a chat still elsewhere The take stopped counting before it held the inventory lock, so a follower emission in that gap sent the old row with taking:false, which a client that stopped waiting reads as a take that did not move the chat. It now stops counting under the lock, and a failed reconcile republishes only a cached row that already says the chat is here. The protocol text states the guarantee the code keeps; the change entry lists its docs surface. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * app: a take that acquired the chat says it runs until a row says the chat is here An error after the chat was acquired, or a presence refresh after a failed reconcile, still republished the row from before the take with taking:false. The take now records the fence it acquired, and every row older than that keeps taking:true until a row says the chat is here or a later fence holds it. The scheduling-dependent lock test is replaced by deterministic ones; the protocol and change entry say when taking ends, and a reopened link counts any approval for this account's devices. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * app: republishing a take's taking leaves agents where the take put them Republishing cached rows for taking also re-placed their local agents, so after a failed reconcile the agent a take had just put here went back to the other computer. That republish now changes rows only. A chat that leaves the list drops its landed take. The round trip checks taking only on the row that says the chat is here, since a lagging row may still say it. The protocol and change entry say a here row can carry taking while a take runs, and that a lost approval reply did let the device join. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * app: a presence change leaves a landed take's agent where the take put it When a take acquired the chat but its reconcile read failed, the cache still said the chat was elsewhere, and the next presence-only frame re-placed the agent the take had put here back on the other computer. That refresh now skips re-placing agents on rows whose take landed and no row has yet confirmed. It skips only those rows rather than every row: presence changes the offline sentence in `line` and `takeover_line`, which an agent's location carries, and nothing else pushes that update to a following client. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * app: a reading from before a landed take leaves the agent here readChats re-placed every row's agent, so a follower reading newer than the cache but older than the take moved the agent agents.take put here back elsewhere. It now skips rows with a landed take, as presence does; a row saying the chat is here clears the landed take first and still places the agent. agents.take is now driven through its acquired-then-failed paths with the copy and open stood in (no engine needed): opening the moved chat fails, and settleTake's reading fails followed by a stale reading. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * app: only a directory reading drops a landed take whose chat left the list publishTaking and the presence refresh mark taking on cached rows, which can be empty while the follower runs; pruning there forgot a take that acquired the chat, so a later reading from before it moved the agent back. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Expose plugin connections through the desktop protocol * Expose plugin connection dates and provider state * fix: prevent cancelled plugin saves and scope asks by agent Keep account lookup and credential lock waits outside the flow mutex. Serialize only the final credential replacement with cancellation so an abandoned browser attempt cannot save later. Scope connection ask ids by agent across plugin, chat and Inbox events. * manual: the desktop page is called Connectors Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: report whether connector keys were checked Preserve unset key variable names in connection refusals. Expose catalog checkability and whether a connection probe passed without guessing vendor endpoints. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: name the pull request in the plugins change entry Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Brings the integration branch up to app/v3 (4 commits: pairing QA fixes for failures, approvals, removed computers and the devices loop; a person's pause survives a lost heartbeat reply; pairing follow-ups where app and pair approve settle a lost reply and chat rows say a take is running; the plugins desktop protocol over the connect layer). One conflict, in cmd/codeaf/app.go: the appBackend struct keeps the client-tools field beside app/v3's plugin pause, Ollama and auth fields. internal/appwire/server.go (both optional hello features), internal/session/connect.go and docs/design/app-v3/PROTOCOL.md merged line-disjoint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Collaborator
Author
|
Landed by fast-forward: Gate run 41762991761:
|
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
Brings the Workstreams integration branch up to
app/v3(4 commits behind: #2285, #2330, #2314, #2331). A merge, not a rebase: no commit on either side is rewritten.cmd/codeaf/app.go: theappBackendstruct keeps the client-tools field beside app/v3's plugin pause, Ollama and auth fields.internal/appwire/server.go(the client-tools and plugins hello features are both optional and sit side by side),internal/session/connect.goanddocs/design/app-v3/PROTOCOL.mdmerged line-disjoint.How it lands
This PR runs the gate only. It is not squash-merged: a squash would flatten app/v3's commits into one and leave app/v3 out of the branch's history, so every later sync would replay them. Once the gate is green (or red only on known baseline failures, confirmed against a fresh app/v3 run),
workstreams/mainis fast-forwarded to this branch's merge commit and this PR closes as merged.Validation
🤖 Generated with Claude Code