Self-hosting survival: persist chat + CLI session IDs across reloads#66
Merged
Self-hosting survival: persist chat + CLI session IDs across reloads#66
Conversation
The critical path for using Klonode to edit Klonode: editing a server-side file (API route, store, config) triggers a Vite dev server restart and full page reload. Before this change, three things died on the reload: 1. Chat history (chatStore.messages was never persisted) 2. Claude CLI session ID mapping (a component-local const in ChatPanel.svelte that reset on every remount) 3. The user had no signal that a response had been interrupted — the loading spinner just vanished With all three lost, every store-file or API-route edit burned all of Claude's accumulated conversation context. Self-hosting was unworkable. ## What now survives - **Chat history** (klonode-chat, last 80 messages). chatStore auto- persists on every update. Transient state (isLoading, error, lastComparison) is left out of the snapshot. - **CLI session ID per tab** (klonode-sessions.cliSessionIds). Moved out of ChatPanel's local const into SessionsState with getCliSessionId / setCliSessionId / clearCliSessionId helpers. The next message after a reload resumes the same Claude conversation rather than spawning a fresh one. - **Per-session message backlog** (sessions.messages, last 50 per session). The agents API / CO analysis path already read from here but it was never persisted. - **Date timestamps** — rehydration now converts ISO strings back to Date objects so .toISOString() / formatting code doesn't throw. - **Interrupted flag** — any message that was `loading: true` at save time gets `loading: false, interrupted: true` on hydrate so the UI can render a "response interrupted by reload" banner instead of a spinner that never resolves. ## What the user sees - A pulsing amber `● streaming` badge in the chat header while a response is in flight, with a tooltip explaining that editing a server-side file will interrupt it. - A `⚠ response interrupted by reload` banner on any assistant message that was cut off by a reload, so it's clear what happened rather than leaving a blank bubble. ## Storage safety - `MAX_PERSISTED_CHAT_MESSAGES = 80` and `MAX_PERSISTED_MESSAGES_PER_SESSION = 50` cap the per-store footprint so a long conversation can't blow through the ~5 MB localStorage quota. - Both save paths catch QuotaExceededError and fall back to persisting session metadata (IDs, active tab, CLI session IDs) without the message backlog, since losing history is much cheaper than losing conversation continuity. ## Documented docs/self-hosting.md explains which edit paths are safe during an active chat (component files → HMR, history survives), which trigger a full reload (stores, API routes, config), and the implementation pointers for anyone debugging a survival regression later.
This was referenced Apr 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
The critical path for using Klonode to edit Klonode. When you edit a server-side file (API route, store, Vite config) while a Claude session is running, Vite restarts the dev server and triggers a full page reload. Before this PR, three things died on that reload:
chatStore.messageswas never persisted. Every reload = empty scrollback.With all three lost, self-hosting was unworkable: one typo in a store file or one edit to `api/chat/stream/+server.ts` burned the entire conversation.
What now survives a reload
UX additions
Verified end-to-end with preview
No console errors. No regressions in the existing UI.
Storage safety
Documentation
`docs/self-hosting.md` explains which edit paths are safe during an active chat (`.svelte` files → HMR, everything survives) and which trigger a reload (`.ts` stores, `api/**` routes, Vite config). Includes a safe-vs-risky table, a risky-edit playbook, and pointers to the implementation files for anyone debugging a survival regression later.
Independent of #65
This branch is stacked on main, not on `feat/workstation-self-introspection`. Both PRs are orthogonal and can merge in either order.
Closes
Addresses the self-hosting blocker the original #53 was reaching for. A new tighter tracking issue for the remaining self-hosting work (stream resume across server restarts, worktree-based safer edit paths) can be filed after this lands.