Skip to content

fix(web): survive page reload without losing analysis or running LLM jobs - #18

Open
jakob1379 wants to merge 1 commit into
jamesaphoenix:mainfrom
jakob1379:t3code/fix-page-reload-drops-process
Open

fix(web): survive page reload without losing analysis or running LLM jobs#18
jakob1379 wants to merge 1 commit into
jamesaphoenix:mainfrom
jakob1379:t3code/fix-page-reload-drops-process

Conversation

@jakob1379

@jakob1379 jakob1379 commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Problem

In the web UI (diffcore-web), a browser reload dropped everything: repo path, branch pair, analysis, and any LLM refinement / deep-analysis job that was still running. The server kept running the jobs and retained their SSE history, but the UI held all of that state only in React memory and never reconnected.

Fix (web only, desktop opts out)

  • Session persistence in sessionStorage (per tab): repo path, base/head, the server's --repo it was recorded under, the repo the current analysis was run for, and whether the branch pair was picked explicitly.
  • Restore on load: once settings are loaded, the analysis is re-run automatically, but only if the stored repo is the one that had actually been analysed. A stored session is ignored when the server's --repo changed, when the field holds an unsubmitted PR URL, or when the record is malformed.
  • Branch pair: explicitly picked branches survive the reload; auto-detected ones are re-detected, so a reload still picks up a git checkout made in a terminal (the web UI has no HEAD watcher).
  • Job persistence: streaming jobs are stored when started (scoped to repo + base + head) and forgotten only on a terminal SSE event. Forgetting on error was wrong: Chromium fires the EventSource error during page teardown, which wiped the list before the new page loaded.
  • Reattach: the newest stored job is probed with a plain fetch (EventSource cannot expose HTTP status), pruned if the server no longer knows it or it belongs to another repo/branch pair, otherwise reattached with the running-state flags restored so cancel works via the stored id. Each stream closes only the EventSource it owns.
  • Refinement results read the current analysis through a ref instead of a stale closure. This also fixes a pre-existing bug where a cached refinement was silently dropped on the first analysis of a page. A live result now wins over a cached one.

Preview

output

Verification

  • Playwright suite: 226 passed, 2 skipped. New session-restore.spec.ts covers repo field + picked branches surviving reload, auto-detect resuming after changing the repo, and auto-detected branches being re-detected after reload.
  • Manual against diffcore-web on this repo with real Claude CLI refinements (the demo-mode e2e suite has no server, so job reattach is manually verified only):
    • reload mid-job: running state and live stream restored, cancel via restored id stops the server job
    • job finishing during the reload window: applied on history replay
    • stale ids (server restarted), foreign-repo and foreign-branch jobs: pruned silently
    • changed --repo: field resets to the server's repo; unchanged: analysis restored without a click
    • half-typed, never-analysed path: restored into the field but not auto-analysed

…jobs

The web UI kept repo, branch pair, analysis, and job ids only in React
state, so a reload dropped everything while the server kept the jobs
running. Persist the session and the active job list in sessionStorage
(web only, per tab), re-run the analysis on load, and re-attach to
stored job streams after probing that the server still knows them.

A job is forgotten only on a terminal event; Chromium fires the
EventSource error during page teardown, so cleaning up on error wiped
the list before the new page loaded. Jobs are scoped to the repo they
were started for, each stream closes only the EventSource it owns, and
a stored session is ignored when the server's --repo changed.

Refinement results read the current analysis through a ref instead of
a stale closure, which also fixes cached refinements being dropped on
the first analysis of a page; a live result wins over a cached one.
@jamesaphoenix

Copy link
Copy Markdown
Owner

Should we store the refinment streaming in SQLite, then we could use:

  • Hydrate from zustand -> API -> DB
  • Catch up on any event's that have been missed with an event-order-id key with a stream-id key?

Thoughts on the above?

@jakob1379

Copy link
Copy Markdown
Contributor Author

SQLite would make sense. I'll get to it soon enough

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants