Session detaches from its conversation after a --no-persist query
- Status: Done
- Kind: Bug
- Authors: jp
- Date: 2026-08-15
- Label: domain=conversation
- Label: package=jp_workspace
- Label: type=bug
Running jp -! q --new ... in a terminal loses that terminal's active conversation. The next jp q in the same tab opens the conversation picker instead of continuing where it left off.
Reproduced by just rfd-summaries, which runs jp -! q --format=json --new once per changed RFD.
Cause
--no-persist swaps the persist and lock backends to their Null variants but leaves the session backend pointing at real storage. Query::run records the session activation unconditionally, so the ephemeral conversation — which is never written to disk — lands at the front of the session's history.
Workspace::session_active_conversation reads history[0] and filters it against the live conversation index. The entry misses, so it returns None and the caller falls through to the picker, even though the real conversation is still sitting at history[1].
Related
Two nearby defects in cleanup_stale_files, both reachable from a --no-persist run whose lock backend reports nothing locked:
- Lock state is read from the workspace's lock backend, so an ephemeral run can delete another process's session mapping while its conversation is mid-write.
- Archived conversations are absent from the live index, so archiving one prunes it from every session's history. Unarchiving restores a conversation the session has forgotten.
Comments
- From: jp
- Date: 2026-08-15T06:17:28Z
Fixed in b90fd2c4.
Three changes:
ReadOnlySessionBackend(jp_storage::backend::null) wraps the real session backend: reads pass through, writes are dropped.load_workspaceinstalls it alongside the existing Null persist and lock swaps, so an ephemeral run can resolve the session's conversation but can't record one.Workspace::resolvable_historybackssession_active_conversationandsession_previous_conversation. It walks the history and returns the entries that still resolve, skipping conversations present in neither the live index nor the archive. The walk stops at an archived entry, so archiving the active conversation leaves the session without a target and the caller opens the picker rather than silently moving the user to an older conversation.cleanup_stale_filesreads lock state from theFsStorageBackendit is already handed instead of the workspace's lock backend, and counts the archive partition as live when pruning history entries.
Tests: five in session_mapping_tests.rs (ghost skipped for active and previous, archived head stops the walk, cleanup keeps archived and mid-write entries), three in null_tests.rs for the read-through/write-discard backend. Each of the five workspace tests was run against the unfixed code and observed red first.
Not covered: an end-to-end test that jp -! q leaves the session file byte-identical. That path needs a live provider, so coverage stops at the storage and workspace layers plus the wiring in load_workspace.