record a resumed session's model when it switches, not when the turn ends
`createMessageHandler` is built per turn, so `transcriptId` started null on every one — and
`applyModelChange` runs before the turn emits anything, so the `model:switched` it produces arrived while
there was nothing to key the row on. A resumed conversation therefore recorded its model only on the closing
`result`, and a turn that died took the switch with it.
Observed rather than reasoned: the sidecar log said `claude-code/fable -> claude-code/opus[1m]` while the
stored row still said `claude-code/opus`.
Seeded from the resume id, which the Claude path already has in scope for the spawn. A brand-new
conversation still waits for `session:claude` — it has no id yet by definition — but resumed is the case
where switching actually happens.
Also lands docs/chat-sessions-list.md: the plan of record for the sessions-list revamp, agreed with the owner
today. Six numbered targets, four decisions with their reasoning, and the landmines — chiefly that merging
the two left-column panels strands `chat-live` in every persisted layout, which is the same stale-appType
shape that made /offscale render three server pickers this morning (ed3806f9). Written down because the shape
came out of a conversation, not out of the code.
This commit is contained in:
@@ -318,7 +318,7 @@ function foldIntoSession(session: UserSession, msg: TurnMessage, model: string):
|
||||
}
|
||||
|
||||
// Shared by both harnesses now: whichever sidecar ran the turn has already translated and committed it.
|
||||
function createMessageHandler(sessionId: string, model: string) {
|
||||
function createMessageHandler(sessionId: string, model: string, resumeSessionId?: string) {
|
||||
/*
|
||||
The model messages are ATTRIBUTED to, which is not always the model the turn asked for.
|
||||
|
||||
@@ -339,7 +339,19 @@ function createMessageHandler(sessionId: string, model: string) {
|
||||
keyed by the transcript id or it will never join to a list row — and the transcript id is not known when
|
||||
the turn starts, only when the harness announces it. That is what this waits for.
|
||||
*/
|
||||
let transcriptId: string | null = null;
|
||||
/*
|
||||
Seeded from the resume id, and that seed is what makes a switch durable.
|
||||
|
||||
This handler is built PER TURN, so without the seed `transcriptId` starts null on every one — and
|
||||
`applyModelChange` runs before the turn emits anything, so the `model:switched` it produces arrived while
|
||||
there was still nothing to key the row on. A resumed session then recorded its model only on the closing
|
||||
`result`, which means a turn that died took the switch with it: the sidecar log showed
|
||||
`fable -> opus[1m]` while the stored row still said `opus`. Observed on this machine.
|
||||
|
||||
A brand-new conversation still has to wait for `session:claude` — it has no id yet by definition — but a
|
||||
resumed one already knows, and resumed is the case where a switch actually happens.
|
||||
*/
|
||||
let transcriptId: string | null = resumeSessionId ?? null;
|
||||
|
||||
/**
|
||||
* Write the model down, once both halves are known.
|
||||
@@ -629,7 +641,9 @@ async function handleClaudeCodeChat(
|
||||
|
||||
session.isGenerating = true;
|
||||
|
||||
const onMessage = createMessageHandler(sessionId, model);
|
||||
// The resume id is passed so a tier switch on a RESUMED conversation is recorded when it happens rather
|
||||
// than when the turn ends — see the seed in `createMessageHandler`.
|
||||
const onMessage = createMessageHandler(sessionId, model, msg.resumeSessionId);
|
||||
|
||||
/**
|
||||
* One description of the turn, used by both paths below.
|
||||
|
||||
Reference in New Issue
Block a user