Spool remote ensure payloads over stdin so big-memory loops reattach - #548
Merged
Merged
Conversation
scgopi
force-pushed
the
fix/remote-reattach-random
branch
from
October 2, 2026 15:24
0dc7af9 to
e7241af
Compare
The daemon's remote ensure carried the CLI shim, the briefing (twice for Copilot) and the loop's wake digest inline in the ssh remote command, which the host runs as a single `zsh -c` argument. Linux refuses one argument over 128 KiB (MAX_ARG_STRLEN) with E2BIG before any of it runs, so loops with a large memory failed every ensure silently: no dial-log line on the host, and after a reboot their panes sat on "reboot wait-daemon" while siblings with smaller memories came back. The ensure now spools every delivered file from the dial's stdin (length and SHA-256 checked, 60s alarm), so its command line no longer grows with the loop's memory. The pane's inline delivery is deflated for margin. A failed ensure is recorded in graphcoded.log with the node, argv and stdin sizes and ssh's output. Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: scgopi <[email protected]>
scgopi
force-pushed
the
fix/remote-reattach-random
branch
from
October 3, 2026 01:21
e7241af to
e8c060b
Compare
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.
Problem
After a codespace restart, some loops never reattach: the host's
dials.logshows onlyreboot wait-daemonfor them, never anensureline, while sibling loops on the same codespace recover.The daemon's remote ensure put the CLI shim (~45 KB), the briefing (twice for Copilot) and that loop's wake digest (up to 40 × 512 B memos + an 8 KB playbook) inline in the ssh remote command, base64'd twice. sshd runs that command as the single
-cargument of the login shell, and Linux refuses any single argument over 128 KiB (MAX_ARG_STRLEN) with E2BIG before a byte of it runs. So:The pane's own dial carries no wake digest, so it stayed under the limit.
Measured on main, worst-case memory: 160,051 bytes (Claude Code) and 174,681 bytes (Copilot) against 131,072.
Fix
RemotePayloadSpool). The command line carries only a byte count and a SHA-256 per payload, so it no longer grows with the loop's memory. A short or garbled read leaves the file empty, which shows up asdelivery install failedin the host's dial log. Stdin is then closed, and a 60 s alarm stops a dial whose input never arrives.MAX_CANON. It is written off the calling task, since a PTY write blocks until ssh reads it.zlib.decompress(…, -15).graphcoded.loggetsevent=remote-ensure-failed node=… argvBytes=… stdinBytes=… output=…. The host's dial log can never record a dial that failed before its script ran.Evidence
RED: main sources + anEnsureFitsOneLinuxArgumentWithAFullMemory via swift test (SwiftPM target over GraphcodeKit) -> FAILED, remote command is 160051 bytes (claudeCode) and 174681 bytes (copilotCLI), limit 131072
GREEN: swift test --filter RemoteEnsureArgumentLimitTests and RemoteSessionLaunchTests on this branch -> 26 tests in 2 suites passed, including a 150 KB+ spooled delivery landing byte-exact through runRemoteRetryingCollecting's PTY
REGRESSION: swift build (GraphcodeKit, graphcode-cli, graphcoded) -> exit 0; swift test RemoteSessionLaunchTests + RemoteSessionResumeTests -> 56 tests in 3 suites passed; swift format lint --strict on touched files -> exit 0
Caveats:
Not in this PR:
CodespaceDialBreakercan strand every loop on a codespace whose daemon reads fail for more than 240 s. Only a human marker or a successful daemon dial clears it, and a pane's successful redial does neither. That needs a separate change.