fix: don't discard codex/gemini/antigravity conversations on Resume; fix DELETE ownership dup + missing broadcast

resumeHistorySession() creates the resumed row in its own mode via a
modeConfigKey map (opencode/pi/grok/omp -> continueSession, deepseek ->
resumeSession) and retires the old row afterward. codex, gemini and
antigravity were missing from that map, so resuming one of their rows
started a brand-new session with NO continuation while still deleting
the row it came from -- silent data loss dressed as the duplicate-row
fix. Gate row retirement on continuesSomething (true only for modes that
actually got a continuation config) instead of wiring an unverified
sessionId->native-conversation-id assumption for the three affected CLIs.

DELETE /api/sessions/:id reimplemented the ownership 404 check inline in
two places instead of going through findSessionOrFail, and its
persisted-only-session branch never broadcast session:deleted, so other
open tabs kept the retired row until their next unrelated fetch. Extract
the shared 404 into sessionNotFoundError(), add findPersistedSessionOrFail()
alongside findSessionOrFail() in route-helpers.ts (same ownership
contract, returns a SessionState instead of a live Session), and use both
from the route instead of inline checks. Add the missing broadcast.
This commit is contained in:
timkjr
2026-08-28 14:03:07 -05:00
parent 2ee2eacb4b
commit f18dccace1
5 changed files with 205 additions and 23 deletions
+4
View File
@@ -356,6 +356,10 @@ describe('session-routes', () => {
expect(body.success).toBe(true);
expect(harness.ctx.store.demoteOrRemoveSession).toHaveBeenCalledWith('ghost-session');
expect(harness.ctx.cleanupSession).not.toHaveBeenCalled();
// Ark0N/Codeman#353 review: the persisted-only branch used to demote/remove
// with no broadcast, so other open tabs kept showing the retired row until
// their next unrelated fetch.
expect(harness.ctx.broadcast).toHaveBeenCalledWith('session:deleted', { id: 'ghost-session' });
});
it('404s a persisted-only session id the state store does not recognize either', async () => {