adopt on a resume even when the harness is a guess
Regression from my own B7 change, reported within the hour: turns collapsing to "turn completed without output" and coming back only on refresh. B7 stopped resume-cursor defaulting an unidentified session to claude-code. Correct for the durable cut-off row, wrong for adoption: useChat sends model only if modelRef.current is set, so a reconnect without one is routine, not exotic. Declining to adopt left the socket unbound to the live session, so the running turn output went nowhere — and a refresh looked like a fix because it rebuilds from the durable log. Adoption is about DELIVERY and must be generous; only the durable write needs certainty. So adopt on the default again, mark it as an assumption, and skip the cut-off check on it. That keeps B7 fixed — no false "agent went away" written against an opencode session — with no unbound sockets. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -28,17 +28,24 @@ describe('decideResume', () => {
|
||||
expect(decideResume(undefined, 'claude-code')).toEqual({ kind: 'adopt', model: 'claude-code' });
|
||||
});
|
||||
|
||||
it('stays unknown rather than defaulting to claude, which is the whole bug', () => {
|
||||
// No record and no claim. Previously this became `claude-code` and was indistinguishable from a real
|
||||
// Claude session; now it declines to adopt and declines to run the cut-off check.
|
||||
expect(decideResume(undefined, undefined)).toEqual({ kind: 'replay-only' });
|
||||
expect(decideResume(undefined, '')).toEqual({ kind: 'replay-only' });
|
||||
it('marks a total unknown as an assumption instead of passing it off as knowledge', () => {
|
||||
// No record and no claim. This still adopts — see below — but the caller can tell it apart, which is
|
||||
// what keeps the durable cut-off row off a session nobody has identified.
|
||||
expect(decideResume(undefined, undefined)).toEqual({ kind: 'assume', model: 'claude-code' });
|
||||
expect(decideResume(undefined, '')).toEqual({ kind: 'assume', model: 'claude-code' });
|
||||
});
|
||||
|
||||
it('never reports replay-only once anything is known, so a live turn is always re-bound', () => {
|
||||
// The failure mode in the other direction: declining to adopt a session we could have identified
|
||||
// would silently drop the reconnect that makes turn output resume.
|
||||
expect(decideResume('claude-code', undefined).kind).toBe('known');
|
||||
expect(decideResume('', 'claude-code').kind).toBe('adopt');
|
||||
it('always yields a model to adopt with, because an unbound socket misses the whole turn', () => {
|
||||
// Regression guard. Refusing to adopt when the harness was unknown looked principled and broke the
|
||||
// app inside an hour: `useChat.ts` only sends `model` `if (modelRef.current)`, so a reconnect
|
||||
// without one is routine. The socket never re-bound, the live turn's output went nowhere, and the
|
||||
// transcript collapsed to "turn completed without output" until a refresh rebuilt it from the log.
|
||||
for (const d of [
|
||||
decideResume('claude-code', undefined),
|
||||
decideResume(undefined, 'opencode/x'),
|
||||
decideResume(undefined, undefined),
|
||||
]) {
|
||||
expect(d.model).toBeTruthy();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user