Ask before switching while sessions are live, and recover the ones already broken
Acts on docs/incident-mode-switch-corrupts-live-sessions.md, which is added here as the record of why. The report identifies a consequence that was not modelled. A failed call is recoverable; a *successful* one may not be. If a running session takes even one completion from the provider being switched to - which happens when that mode matches the base URL it already had cached - that provider's message-id format lands in its transcript. OpenRouter issues `gen-<epoch>-<rand>` where Anthropic issues `msg_...`, and native Anthropic then refuses to resume the session at all, with a 400 naming previous_message_id. The only way back is to truncate the transcript, losing every turn after the cut. That happened here, and was fixed by hand. Two changes follow. Sessions are now settled before the write, not reported after it. A switch with anything running stops, names the sessions, explains what is about to happen to them, and offers restart (the only answer that ends with everything on the mode the bar now claims), close, proceed anyway, or abort - defaulting to abort. Non-interactively it refuses outright unless given --yes. The old after-the-fact reporter is deleted rather than left as a second, contradictory account. `claude-mode repair-session` replaces the hand surgery: it finds a project's transcripts, reports which are resumable, and on --apply backs the file up and truncates to the last Anthropic-issued message. Verified against the real corrupted transcript from the incident - it reproduces the manual cut exactly, 1921 lines to 1813, dropping the two `gen-` completions and the error placeholders after them, leaving a transcript that ends on a genuine msg_ id. It refuses a transcript written to in the last 90 seconds, since that one belongs to a session still running. The panel passes --yes, having already asked in its own card, and that card now names the transcript risk rather than only the inconvenient one. Requirement 4 of the report - documenting the mechanism - landed in 9c301e1; the README now carries the unrecoverable half as well.
This commit is contained in:
@@ -322,8 +322,38 @@ switch reaches into a live session through the one thing that was never cached:
|
||||
| another preset of the *same* provider, same `keyRef` | same key, same endpoint — this one survives, on the model ids it started with |
|
||||
|
||||
Either of the first two starts failing calls whenever the TTL happens to expire:
|
||||
mid-turn as easily as between turns. So restart afterwards — and on Linux,
|
||||
`claude-mode sessions --restart` will do it for you:
|
||||
mid-turn as easily as between turns.
|
||||
|
||||
### The part that is not just an inconvenience
|
||||
|
||||
A failed call is recoverable. A *successful* one may not be.
|
||||
|
||||
If a running session takes even one completion from the new provider before
|
||||
anything notices — which happens when the mode it is switched to matches the
|
||||
base URL it already had cached — that provider's message-id format lands in its
|
||||
transcript. OpenRouter issues `gen-<epoch>-<rand>` where Anthropic issues
|
||||
`msg_…`. Native Anthropic then refuses to resume the session at all:
|
||||
|
||||
```
|
||||
API Error: 400 diagnostics.previous_message_id: must be the `id` from a
|
||||
prior /v1/messages response (starts with `msg_`)
|
||||
```
|
||||
|
||||
There is no supported way back from that. The only fix is to truncate the
|
||||
transcript to the last message Anthropic issued, losing every turn after it:
|
||||
|
||||
```bash
|
||||
claude-mode repair-session # list transcripts here and their state
|
||||
claude-mode repair-session <session-id> # show what it would cut
|
||||
claude-mode repair-session <session-id> --apply
|
||||
```
|
||||
|
||||
It backs the original up first, and refuses to touch a transcript that was
|
||||
written to in the last 90 seconds, since that belongs to a session still alive.
|
||||
|
||||
This is why a switch now asks before it writes rather than reporting afterwards.
|
||||
So restart affected sessions — and `claude-mode sessions --restart` will do it
|
||||
for you:
|
||||
|
||||
- **CLI** — exit and relaunch `claude`
|
||||
- **VS Code** — `Ctrl+Shift+P` → *Developer: Reload Window*
|
||||
@@ -506,6 +536,11 @@ endpoint is fixed at startup but their credential is re-fetched on a timer, so
|
||||
it switches under them and the endpoint they are still pointed at refuses it.
|
||||
Which is why this is a decision rather than a notification.
|
||||
|
||||
A switch stops and asks while any are running, offering to restart them (the
|
||||
only answer that ends with everything on the mode the bar now claims), close
|
||||
them, proceed anyway, or abort — and abort is the default. Non-interactively it
|
||||
refuses outright unless given `--yes`.
|
||||
|
||||
```
|
||||
claude-mode sessions
|
||||
running sessions (2)
|
||||
|
||||
Reference in New Issue
Block a user