Commit Graph
2 Commits
Author SHA1 Message Date
pastilhasandClaude Opus 5 9203472be8 the recovery could never have fired — fix its condition
Shipped e6422052 an hour ago claiming a chat that cannot be resumed would recover on
the next message. It would not have. Caught before it was pulled, by tracing the two
error strings in a screenshot back to their sources instead of trusting the change.

`sawOutput` was set on ANY SDK message. But a turn that cannot load its conversation
still emits a terminal `result` with `is_error` and no text — that is exactly what
reaches the user as "Claude Code returned an error", one of the two lines in the
report. So the single event that proves the resume failed was being counted as proof
it had worked, and `clearResume` could never be true in the one situation it exists
for.

The flag now ignores `error` and counts everything else, as `countsAsOutput` — a named
function with tests rather than an inline `!==`, because this is the second time this
rule has been wrong and both times it was wrong silently.

`stopped` deliberately counts: an interrupted turn was running, so its conversation
loaded, and discarding a transcript because somebody pressed stop quickly would be a
new bug of the same shape.

Five more tests, including the reported failure end to end (`['error']` -> give up on
the resume) and its opposite (`['assistant:text', 'error']` -> keep it).

Still does not explain WHY the resume fails. Also worth recording: my evidence that
"resume works by hand" may be worth nothing — `claude --resume` with an id it cannot
find may simply start a fresh session and exit 0, which would look identical to
success in both tests I ran. The new spawn log is what will settle it.

tsgo clean, 950 pass / 7 fail unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 20:41:47 +00:00
pastilhasandClaude Opus 5 e642205207 a chat that cannot be resumed stops being permanently dead
A member's conversation answered "Claude Code process exited with code 1" to every
message, four times identically, and could never recover. A brand new chat worked
throughout. Diagnosing it took an evening and produced two wrong answers — the
credential, then the working directory — both disproved by hand on the server,
because the one input most likely to be at fault appeared in no log anywhere.

Three changes, and the first is the one that matters most next time.

── Say what is being run ──

`[claude:spawn:<key>] resume=… cwd=… as=… model=…`, before the spawn. The resume id
is NOT the id in the URL — it comes from a stored map, so the two can disagree, and
nothing could previously show that they had. Every hypothesis about it had to be
tested by hand.

── Carry the child's reason to the person who hit it ──

stderr was logged and then dropped, so the client got a number. The tail is now kept
on the session and travels with the exit, both to the log and to the user. When the
child says nothing at all — which is what happened here — the sentence still reads,
rather than trailing a dangling colon.

── Stop retrying a resume that cannot work ──

`sawOutput` records whether the child ever produced a single message. False, on a turn
that was resuming, means it never got as far as loading the conversation — and since a
fresh chat works with the same process, account, credential and binary, `--resume` is
the only input that differs. So the stored id is dropped and the next message starts
clean.

The history is lost to the agent, which is a real cost, and strictly better than a
thread that can only ever return an error. Nothing reconsidered that id before, which
is why four identical failures in a row was the floor rather than a phase.

Deliberately NOT done when the child spoke first: that is a turn that broke halfway,
the transcript is fine, and clearing it would trade one bad turn for an amnesiac
conversation. That negative case is the second test.

`diagnoseExit` is its own module because everything around it needs the SDK to
construct — which is how the old single sentence went unexamined for so long. 8 tests.

This does NOT explain why the resume failed; it makes the next occurrence say so
instead of costing an evening. The cause is still open.

tsgo clean, 945 pass / 7 fail unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 20:34:37 +00:00