Repository navigation
Fix what the Mac tester found: a deleted home is never rewritten, macOS test reds, provenance on private apply - #2086
Merged
Merged
Conversation
The release-surface step ran go test "${test_args[@]}" under set -u. With no
known-red ledger the array is empty, and /bin/bash 3.2 on macOS treats that
as an unbound variable, so the prologue test failed on a Mac. The conditional
expansion behaves the same on bash 5 and is safe on 3.2.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Two fixtures built Sync{} with an empty Home. A refusal retires the send
hold, which joined the empty Home and wrote v3/sync/holds/<hash>.lock into the
package's source directory on every run. They now use t.TempDir().
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ranches These tests need a furrow engine. GitHub's full tests stage none, so they skip there, and they failed on every machine that has one (a Mac after make build, or CODEAF_FURROW on Linux): - the scripted drive chat wrote files but never a reply, and a chat with no conversation is deliberately not published, so its turns never became durable; each scripted turn now writes a reply to the transcript - the large-home startup test drained a warm capture that has no turn, so nothing was sealed; it now completes one recorded call first - a refused drive branches asynchronously, so the counts and two-home tests wait for the branch row instead of reading it once - the superseded-drive test keeps A offline until its next publish, as the scenario states, and waits for the kept-work notice too Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A codeaf engine that was still saving after it was asked to stop could find its home deleted and treat that as a first run: identity.EnsureSolo made the folder again and wrote a new identity.json, device.json and identity-solo, and the vault capture then wrote the sync ledgers beside them. The Mac tester saw exactly that 8-22 s after removing a test home. Each codeaf process now records the home it starts in (home.Witness). The identity writers, the vault open, the stats recorder and the send-hold writer refuse with "codeaf home was removed or replaced" once that folder is gone or is a different folder, and they never re-create a recorded root. A first run, where no home existed at start, creates it exactly as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An engine asked to stop closes every conversation before it exits, and for a synced chat that can take tens of seconds while the relay is unreachable. If the home was removed meanwhile, everything it still wrote went into a folder that no longer existed. While shutting down, the host now watches its home; if it is removed or replaced, the engine exits within about a tenth of a second instead of saving into it. With the home intact the stop does what it did before, in the same order and with the same limits. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#2078 named the process holding a busy engine store. On macOS it asked lsof about every lock file it found, 500 ms each with no cap, so a failing seal could add seconds. One shared budget now covers the whole lookup, and it stops at the first busy holder; when the budget runs out the message is the plain busy sentence. Requests that are not busy never inspect anything. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
runChatV3Once returned before the interactive road registered its process cleanup, so a paired --once never closed its sync drive. Publishing uploads a chat's frames before it registers the chat with the relay, and a --once that exited in between left the uploads with no record pointing at them: the other computer never listed the chat, and doctor said nothing (Hello saw it in 1 of 48 runs, about 1 s after pairing). --once now always registers drive cleanup; with no drive it returns at once, and a paired chat gets its final send and release inside the same limit the interactive road uses, with one plain stderr sentence if that fails. A paired drive records a small local publication marker, so codeaf doctor can report a chat this computer started that the relay does not list, and doctor --fix registers it through the ordinary road, create-only, never over another computer's record and never for a chat put away meanwhile. A never-paired computer writes no marker and makes no relay request. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…at did not arrive, a deleted home is refused Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
macOS names the Option key opt, so Home's continue offer shows opt+c and the Memory switch shows opt+u. Two tests hard-coded alt+. The Memory test now builds its expected line through the app's own chord spelling, and the takeback test waits for the offer's question and checks a continue chord is drawn beside it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The quiet case removed a paired machine's folder and created a solo identity at the same path in the same process. codeaf now refuses to create an identity in a home it saw and that was then deleted, so the case uses a fresh folder, which is what a never-paired home is. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
42 internal/session subtests failed on macOS and passed on Linux: - On macOS a new file takes its folder's group, so the Apply fixtures' files often carried a group other than the process's and the portable-metadata check refused them during setup, before the test's own behaviour ran. The success fixtures now give their files the process's uid and gid, resolve temp-path aliases first, and clear extended attributes. The product refusal stays: private Apply stages replacements with the process's group, so a foreign group cannot be carried. A new setgid-directory test pins that refusal on Linux. - Bare mktemp on macOS ignores TMPDIR, so five scratch probes now use mktemp "$TMPDIR/codeaf-runtime.XXXXXX". - Two path checks compared a canonical path with a raw temp path; both sides are canonicalized now. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… chat that did not arrive, a deleted home is refused" This reverts commit f28b0f4.
…ers" This reverts commit f9bb2e7.
…ts home is removed Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
macOS 15 stamps com.apple.provenance on every file a process tree started from some apps creates, and the owner cannot remove it. Private apply refused any extended attribute, so on such a Mac it refused every changed file. That one attribute is now tolerated on macOS: the replacements apply writes come from the same process tree and are stamped the same way. Every other attribute, quarantine included, is still refused, and Linux is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AbirAbbas
marked this pull request as ready for review
October 31, 2026 05:23
This was referenced Oct 31, 2026
AbirAbbas
added a commit
that referenced
this pull request
Oct 31, 2026
…2092) * test: remote-task session fixtures pass on macOS temporary folders Two #2085 fixtures failed on macOS only. The r8b home was made under /tmp, which macOS links into /private/tmp, and owned conversation storage refuses a recorded binding whose spelling crosses a link, so newAgent stopped with "workspace binding traverses a symbolic link"; the fixture now names its home by its resolved path. The private-task Review and Apply test wrote its returned file inside a copy under the temporary home, where macOS hands a new file its folder's group (wheel under /private/tmp), and Apply refused that group; the fixture now gives that file the process's own group, as #2086's Apply fixtures do. Both failures reproduce on Linux with a home reached through a link and a setgid TMPDIR owned by a secondary group. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: change entry for the macOS remote-task fixture fix Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <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.
Fixes what the two-network relay test (lane L3, re-check on
72a2792e6) found on the client side. One PR, split by concern: D1, P, the continue fixes (D6, D3, D5, D7), a fix round, and the change entry.What each defect was, and what changed
D6: the chat a person picked was not the chat that opened. A bare
codeaf chatcontinues the newest chat another computer let go of. While that automatic move ran, every explicit Enter or confirm on Home answereda chat is already continuing here; wait for it to open, and the older chat opened. Now a confirmed different chat waits with the holder the person saw (continuing <Y> next; <X> is still on its way here). The automatic move finishes, is let go without opening (through the path a closing window uses, so a shared session is never closed), and then the picked chat is taken and opened. Confirming the arriving chat joins its move. Only a confirm abandons the automatic move; opening a card and answering "leave it there" does not. An abandoned move is bounded (2 min, then cancelled the way Escape used to). With no input, a bare start behaves as before.D3: a false "another device" refusal, and no retry card.
ErrLeaseHeldwent to a footer-only path that skipped #2078's card. The likely race: after a move, the receiving computer's writer re-acquires, which raises the ownership number, and a slow relay's stale listing gives the other side a card with the old number. Now every refusal keeps the1 retry/2 leave it therecard and the draft. A fresh reading separates the same holder reopening (the card asks again, with what it now shows) from a real different device (the real refusal, on the card). Consent still binds holder and ownership number. Reproduced with the two-home rig inTestAReturnAfterTheWriterReopensIsNotALostRace.D5: Home gave up on a slow relay, and the first message after a move stayed unsent. Home's listing had 5 s. The send check ran its own 5 s directory read while the arriving writer was still starting, and it printed Home's timeout sentence into the chat. Now Home's listing waits up to 25 s, shows
waiting for <host>; still checkingat 5 s, and keeps its rows. A real outage (refused, DNS, TLS, 5xx) still shows at once and retries on the old schedule. A send right after this window's own move waits up to 30 s for its writer, off the UI loop, then sends once. Displacement or another window's hold still refuses. A send that cannot leave says<host> has not answered; your message is still in the box, press enter to send it.D1: a directly exposed
codeaf relaytrusted any X-Forwarded-For. Registration and dial limits counted a caller-supplied header. Nowrelay.Servercounts the socket peer unless it is given a policy, and--trust-proxy(default off) applies one policy to registrations, dials, the pairing mailbox and link pairing. A relay behind a proxy must now pass--trust-proxy, or all its callers share one allowance. Pairing already behaved that way without the flag. Flag help,docs/relay.mdand the relay manual page say so.P (from the Cloudflare audit): a chat this computer never held came over serial object batches.
SyncEngine.Holdsread the store-wide publication ledger, and new chats share one store, so every new chat looked warm and skipped its frame plan. Holds now asks about this chat (its adoption record or a recorded turn), and a return into a fresh staging folder asks about the chat's existing folder. In the fixture, a cold 2 MiB chat in a used store went from 7 object POSTs to 1 frame GET, and a warm one-file return still moves 5.9 KB. Correctness rests on the unchanged completeness loop. Known perf-only edge cases (follow-up): after a failed take whose staging was removed, the next take re-downloads frames, and chat metadata restored into an empty store reads warm (today's behaviour).D7: slow empty-folder moves. The cause is not established. Each continue now writes one
chat.logline with per-phase durations. First data, from the hand walk on a local relay with the laptop under build load: time spread acrossdirectory(0.3–7.3 s),restore(0.3–11 s) and staging (1–7 s). No fixed timer showed up; it looks disk- and relay-bound under load.Proof
72a2792e6): Y opened 1 of 3. X opened 2 of 3 with the busy line, and X was left running on B.make test-laws: LAWSNot in this PR (follow-ups)
authorizepasses, each adevice_seencommit plus a barrier, about 2.7 s per request on the dev relay. Ranked causes and the Worker follow-ups are for the relay owners: measure one request inwrangler dev --local, drop the post-I/Odevice_seencommit, coalescedevice_seen, and name theunavailable()branch in 503 bodies. No Worker change in this PR.continuation needs a choice for changed path: .git/COMMIT_EDITMSG(seen in L3's runs and in this lane's first walk)..gitinternals probably should not count as local changes.Stability
Nothing changes for a person who never races a continue, never hits a slow relay, and does not run a self-hosted relay behind a proxy. No change to
internal/clusterstate, wire or stored formats,relay/hosted/, or workflows. HELLO🤖 Generated with Claude Code