Skip to content

Resume a local Copilot loop instead of relaunching it under the same name - #248

Merged
scgopi merged 2 commits into
mainfrom
fix/duplicate-session-launch
Sep 2, 2026
Merged

scgopi merged 2 commits into
mainfrom
fix/duplicate-session-launch

Conversation

@scgopi

@scgopi scgopi commented Sep 2, 2026

Copy link
Copy Markdown
Owner

Summary

A user's dials.log showed loops resumed by the daemon after every restart and nothing else. No duplicate was visible in it because the launch that makes one never wrote a line.

Copilot has no hook to bank its resume id, and only the remote path banked it from outside. Locally the daemon rediscovered the session directory on every ensure, while the app's open path read only the banked pointer. So any local Copilot loop whose zmx session was gone (a reboot, or a turn-based loop, which the daemon never ensures) was relaunched by the pane as copilot --name graphcode-<id> from its goal: a second Copilot session for one loop. The daemon's next resume then picked the newest directory and orphaned the original.

  • Local Copilot id banking in the daemon, the twin of remoteIDBankFragment: after a resume that took, and after a fresh launch once the new directory appears. Same bank copilot-id dial-log line as remote.
  • The pane makes the same choice from the same lookup (ZmxSessionLauncher.resumableSessionID), banking a discovered id before its script reads it.
  • Husk-aware pane check. An agent that died leaves its shell at a prompt, which answered zmx get, so the pane joined the corpse. The pane now uses the daemon's ended=-aware check, kills the husk, and proceeds as if the session were gone, so the resume-or-fresh verdict stays measurable.
  • Every local agent launch goes through the one script and logs its branch, so open fresh and open husk-killed appear in the dial log where the fresh launch used to be silent. The script runs under /bin/zsh because the alive check uses $'\t'.

Not in this PR, noted by a peer loop on the board: the daemon's alive check swallows zmx ls errors, so a transient zmx failure reads as "missing" and zmx run types a second launch into a live session.

Verification

  • xcodebuild test in the worktree: 1479 passed, 0 failed (5 new or updated tests in LocalSessionResumeTests and DialLogTests).
  • swiftlint lint: 0 errors. swift format lint --strict: clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Tqv8Rx8SGsLCjJQYCUjiFr

scgopi and others added 2 commits September 1, 2026 21:59
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tqv8Rx8SGsLCjJQYCUjiFr
…name

A user's dials.log showed loops resumed by the daemon after every restart
and nothing else — no duplicate visible, because the launch that makes one
never wrote a line. Copilot has no hook to bank its resume ID, and only the
remote path banked it from outside; locally the daemon rediscovered the
session directory on every ensure while the app's open path read only the
pointer. Any local Copilot loop whose zmx session was gone (a reboot, or a
turn-based loop, which the daemon never ensures) was relaunched by the pane
as `copilot --name graphcode-<id>` from its goal: a second Copilot session
for one loop, and the daemon's next resume picked the newest directory,
orphaning the original.

- The daemon banks a Copilot session's directory name locally, the twin of
  the remote bank fragment: after a resume that took, and after a fresh
  launch once the new directory appears.
- The pane makes the same choice from the same lookup
  (`ZmxSessionLauncher.resumableSessionID`), banking a discovered ID before
  its script reads it.
- The pane's liveness check is the daemon's husk-aware one. An agent that
  died leaves its shell at a prompt, which answered `zmx get`; the pane
  joined the corpse. The husk is killed and the launch proceeds as if the
  session were gone, so the resume-or-fresh verdict stays measurable.
- Every local agent launch goes through the one script and logs its branch,
  so `open fresh` appears in the dial log where it used to be silent.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tqv8Rx8SGsLCjJQYCUjiFr
@scgopi
scgopi merged commit bb2d859 into main Sep 2, 2026
1 check passed
scgopi added a commit that referenced this pull request Sep 2, 2026
Revert the local Copilot resume work (#248, #249) back to beta7
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant