Conversation
…h agent_create agent_create has refused a missing folder since it was written, before opening any pane. Restore never made the same check. Sharing the test is what keeps the two from drifting on what available means, and gives the refusal a sentence worth rendering. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three changes to one path:
* the launch is refused at build time, so no pane is opened for a folder that cannot work, and
the failure names the folder instead of blaming the engine;
* the fresh retry builds BEFORE it unbinds, so a launch that cannot be built stops costing the
agent the conversation it was resuming;
* a row that failed only because its folder was missing is retried once the folder is back --
the one recorded failure whose precondition is observable from outside the agent.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
0xR32
force-pushed
the
fix/restore-missing-workdir-cli
branch
from
September 17, 2026 08:02
778a16f to
423e892
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.
What does this help someone do?
Keeps an agent whose working directory was deleted while it was not running — a removed git
worktree is the common one — from becoming a dead pane nobody can explain or recover.
A Mac rebooted. The daemon restored eight agents; seven came back. The eighth left this:
The day before, a different agent in the same repository had been asked to clean up merged
branches and ran
git worktree remove .claude/worktrees/feat-org-links. Nothing connected the two.What the restore did with that, from
harness.log:Four things went wrong, and only the first is the missing folder:
restoreAgentsnever checkedcwd. Theshell's
cdguard failed, it exited 1, andremain-on-exitkept the corpse.relaunchFreshonly drops--resume.relaunchFreshcallsunbindSessionbefore itbuilds, so the row's
sessionIdbecame""andtranscriptPathnull— for a retry that neverhad a chance. The transcript survived on disk; the agent no longer pointed at it.
launch: failed, and every later restore skips it.The diagnosis that reached the user was
ENGINE_DID_NOT_START— "claude exited before its engineprocess became ready. See the terminal output for details." The real cause was one line above, in a
pane, on a machine the reader may not be sitting at.
agent_createalready gets this right: it refuses a missing folder withCWD_NOT_FOUNDbeforeopening any pane, and the app already has copy for that code. This applies the same contract to the
one path that skipped it.
The change, in three parts:
workdirAvailable.ts) so create and restore cannot drift on what"available" means.
agent_create's inlinestatSyncnow calls it, and gains adetail.buildLaunchalready has an error channelrestoreAgentshandles, so the row is marked
CWD_NOT_FOUNDwith a sentence naming the folder, no pane isopened, and
unbindSessionis never reached.restoreAgents.ts.relaunchFreshbuilds before it unbinds, so a launchthat cannot be built stops costing the agent its session (this also covers a folder deleted while
the agent runs). And the pre-flight skip gains exactly one exception: a row that failed with
CWD_NOT_FOUNDwhose folder is back is retried instead of skipped — the one recorded failure whoseprecondition is observable from outside the agent. Put the worktree back, restart the daemon, and
the agent returns on its own.
No fallback directory, deliberately: an agent that silently starts in the parent of its deleted
worktree is an agent committing to the wrong branch.
How to try it
Create an agent in
/tmp/repoint-demo/.claude/worktrees/feat-demo, let it start and bind a session,then stop the daemon, end the agent's process, delete the worktree, and start the daemon again.
Before:
→ a dead pane,
sessionId: "",transcriptPath: null.After:
→ no pane opened,
sessionIdandtranscriptPathintact, and the app's "Start failed" notice showsthat sentence (it already renders
launchDetailverbatim, so no app change is needed).Then
git worktree add .claude/worktrees/feat-demo feat-demoand restart the daemon: the agent comesback by itself —
restored 1 · engine up.What did you verify?
macOS 15 (Darwin 25.6.0), Node 22, tmux 3.7c, engine
claude2.1.274.cd cli && npm run typecheck— cleancd cli && npm test— 2,896 passed, 51 skipped, 0 failed (rebased ontomainate6a433e)cd cli && npm run test:tmux-real— 7 passed, 9 engine rows unavailable (not installed here:kilo, grok, copilot and others; an unavailable row is not a passing one)
~/.harnessfolders, both against a purpose-builtagent and against the actual agent from the incident above. Same registry, same input, two
daemons two minutes apart: the installed build produced
ENGINE_DID_NOT_STARTand destroyed thesession binding; this build produced
CWD_NOT_FOUND, opened no pane, and keptsessionIdandtranscriptPath. Recreating the worktree brought the agent back on the next start(
engine up · 12405ms) with no other action.New tests:
workdirAvailable.spec.ts(6) and four inrestoreAgents.spec.ts— a missing-cwd row ismarked
CWD_NOT_FOUNDandcreatePaneis never called and itssessionIdsurvives; the freshrelaunch keeps the binding when the rebuild fails; the self-heal fires for
CWD_NOT_FOUNDonly. Oneassertion was added to an existing fallback test to pin that the binding is still discarded on the
path that fallback was written for — a resume id the engine refuses.
Known limitations
ENGINE_DID_NOT_STARTby an older build stays skipped; onlyCWD_NOT_FOUNDself-heals. Rows that failed before this lands need the agent recreating.
agent_repoint(point a stopped agent at another folder from the app); it is held back deliberately, because it
puts a new type in
ENCRYPTED_DOWN_TYPESand so re-pins thee2ee/core.tsinterop keystone thatthe browser client and the paired device share. That coordination should not ride along with a bug
fix. Nothing in this PR touches
core.ts.🤖 Generated with Claude Code