Skip to content

Complete SSE refactor: migrate consumers, extract invalidators, drop singleton#9261

Open
ericpgreen2 wants to merge 14 commits intoericgreen/sse-client-cleanupfrom
ericgreen/sse-consumer-migrations
Open

Complete SSE refactor: migrate consumers, extract invalidators, drop singleton#9261
ericpgreen2 wants to merge 14 commits intoericgreen/sse-client-cleanupfrom
ericgreen/sse-consumer-migrations

Conversation

@ericpgreen2
Copy link
Copy Markdown
Contributor

@ericpgreen2 ericpgreen2 commented Apr 20, 2026

Stacked on #9260 (merge that first). Migrates the three SSE consumers — FileAndResourceWatcher, Conversation, and ProjectLogsPage — onto the refactored client.

Notable

  • Completes the migration started in Refactor SSE client: tighter boundaries, new lifecycle and subscriber layers #9260 — no consumers remain on the legacy SSE API surface (old import paths, auto-close API, heartbeat() alias).
  • fileAndResourceWatcher is now scoped per runtime (project × branch) instead of a process-wide singleton, so cloud sessions that switch between deployments get their own watcher.
  • Cloud editor sessions survive JWT expiry — the watcher refreshes auth before each reconnect, so long-running edit sessions no longer drop into a reauth wall.
  • Watcher invalidation logic is now exercisable in isolation — the DAG-invalidation dispatcher moved to standalone modules with unit tests, so regressions are caught without mounting the SSE stack.

Checklist:

  • Covered by tests
  • Ran it and it works as intended
  • Reviewed the diff before requesting a review
  • Checked for unhandled edge cases
  • Linked the issues it closes
  • Checked if the docs need to be updated. If so, create a separate Linear DOCS issue
  • Intend to cherry-pick into the release branch
  • I'm proud of this work!

Developed in collaboration with Claude Code

Completes the SSE refactor by moving the three consumers onto the new
layered API, extracting pure invalidator modules, wiring the cloud
editor's JWT refresh, and dropping the singleton/compat shims from PR 1.

Pure extractions (testable, injectable deps):
- `runtime-client/invalidation/file-invalidators.ts` — file WRITE/DELETE
  handling, rill.yaml side effects, throttled `ListFiles` refetch.
- `runtime-client/invalidation/resource-invalidators.ts` — per-kind
  dispatch for Connector, Source/Model, MetricsView, Explore, Canvas,
  and Component resources plus delete variants.
- `web-admin/.../status/logs/log-store.ts` — ProjectLogsPage state
  machine (monotonic ids, ring buffer, filter-by-level + search).

Consumer migrations:
- `FileAndResourceWatcher` is a thin per-mount class over
  `SSEConnection` + `SSESubscriber` + optional `SSELifecycle`. The
  singleton export is gone; the Svelte component constructs the watcher
  synchronously so `setContext` runs before descendants read it.
- `RuntimeTrafficLights` reads watcher state via the new
  `watcher-context.ts` key, with a safe fallback so it still renders
  outside a watcher provider.
- `Conversation` uses `SSESubscriber` for typed message/error routing;
  untagged frames normalize to the "message" decoder per the SSE spec.
  Transport vs. server errors stay on separate channels.
- `ProjectLogsPage` uses `SSEConnection` + typed `SSESubscriber<{ log,
  error }>` and delegates state to `LogStore`.

Call-site updates:
- `web-local/src/routes/+layout.svelte` → `lifecycle="aggressive"`.
- `web-admin/.../edit/+layout.svelte` → `lifecycle="none"` plus an
  `onBeforeReconnect` hook that invalidates the branch-scoped
  `GetProject` query key so the next `runtimeClient.getJwt()` returns a
  fresh token.

Also:
- `SSEConnection.handleError` now fires `close` when `retryOnError` is
  false so one-shot consumers (chat) can settle cleanly on failure.
- Deprecation shims removed: `sse-fetch-client.ts` and
  `sse-connection-manager.ts` at the old paths; auto-close methods and
  the `autoCloseTimeouts` param stripped from `SSEConnection`.
- New specs: `file-invalidators.spec.ts`,
  `resource-invalidators.spec.ts`, `file-and-resource-watcher.spec.ts`,
  `RuntimeTrafficLights.spec.ts`, `conversation-streaming.spec.ts`,
  `log-store.spec.ts`. `web-admin/package.json` gains a `test:unit`
  script for the latter.
…to ericgreen/sse-consumer-migrations

# Conflicts:
#	web-common/src/runtime-client/sse/sse-connection.ts
…er-migrations

# Conflicts:
#	web-common/src/runtime-client/sse/sse-connection.ts
With all consumers migrated off the deprecated auto-close API in the
previous commit, the last PR 1 shims can go:
- heartbeat() alias on SSEConnection (use resumeIfPaused() directly)
- SSEConnectionLifecycle's schedulePause / cancelScheduledPause are
  now private; no external caller drives them
@ericpgreen2 ericpgreen2 changed the title SSE consumer migrations + singleton removal Complete SSE refactor: migrate consumers, extract invalidators, drop singleton Apr 21, 2026
@ericpgreen2 ericpgreen2 self-assigned this Apr 21, 2026
This keeps connection/subscriber concerns separated while reducing boilerplate in watcher, chat, and logs consumers.

Made-with: Cursor
Reintroduce Playwright reconciliation logging in the resource watcher and surface subscriber JSON parse failures as chat stream errors to avoid silent drops.

Made-with: Cursor
Make cloud edit-session reconnect invalidation track latest reactive route values, ensure FileAndResourceWatcher always delegates to the latest callback prop, add an instanceId/runtimeClient consistency guard, and clarify legacy resource invalidation gate naming and test wording.

Made-with: Cursor
Inline the trivial per-kind write/delete helpers into `dispatchWrite` and
`dispatchDelete`, and inline `handleFileWrite`/`handleFileDelete` into
`handleFileEvent`. Drop `isLeafResource` as an injected dep and call it
directly from `invalidateForSourceOrModelWrite`.
@ericpgreen2 ericpgreen2 requested a review from AdityaHegde April 21, 2026 12:21
@ericpgreen2 ericpgreen2 marked this pull request as ready for review April 21, 2026 12:27
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.

1 participant