feat: live sandbox updates via WatchSandbox WebSocket relay (FEATURE_LIVE_UPDATES) - #33
Open
Gkrumbach07 wants to merge 4 commits into
Open
feat: live sandbox updates via WatchSandbox WebSocket relay (FEATURE_LIVE_UPDATES)#33Gkrumbach07 wants to merge 4 commits into
Gkrumbach07 wants to merge 4 commits into
Conversation
…LIVE_UPDATES)
Relay the gateway's WatchSandbox server stream over a WebSocket so the
sandbox detail page — draft-policy inbox included — updates sub-second
instead of waiting out the 5-10s poll intervals.
- BFF: gateway.WatchSandbox wrapper, watch_handler.go WS relay
(follow_status always, follow_logs opt-in, ping/pong keepalive),
WatchEvent DTO through models.From*() so secret-stripping applies,
route gated by FEATURE_LIVE_UPDATES
- Frontend: useSandboxWatch hook pushes snapshots into the React Query
cache and debounces draft/policy invalidations; useSandbox and
useDraftPolicy accept {live} to suspend refetchInterval while the
socket is open, so polling remains the automatic fallback
(reconnect with capped backoff, give up after repeated failures)
- Live/Polling indicator on the detail page header
- Docs: FEATURE_FLAGS.md, ADR 0004, streaming-RPC rule update
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JkEomBo8gaAzDj3RpVFtF5
Signed-off-by: Gage Krumbach <gkrumbach@gmail.com>
Phase 3 of live updates (ADR 0004): - Extract the reconnecting-socket logic shared by both live hooks into hooks/watchSocket.ts - useSandboxLogStream opens its own watch socket with logs=true (tail replay + live lines, buffer capped at the selected line count, buffer reset on reconnect so replays don't duplicate) - SandboxLogsTab prefers the stream and shows a Live label in place of the auto-refresh checkbox; the polled query stays as fallback, and a failed poll no longer clobbers a healthy stream - watch_e2e_test.go: end-to-end over a real gRPC server, real gateway.Client, real router, and a real WebSocket client — asserts snapshot and log frames relay through the whole chain, and that the watch route is absent when FEATURE_LIVE_UPDATES is off Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JkEomBo8gaAzDj3RpVFtF5 Signed-off-by: Gage Krumbach <gkrumbach@gmail.com>
- Split the watch relay handler into buildWatchRequest / startWatchReader / pumpWatchEvents / writeWatchFrames (gocyclo) - Extract requireWatchRequest helper in the e2e test (gocyclo) - Reorder WatchEvent fields pointers-first (govet fieldalignment) - Prettier-format the new frontend files Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JkEomBo8gaAzDj3RpVFtF5 Signed-off-by: Gage Krumbach <gkrumbach@gmail.com>
CI runs go test -race: the tests polled a variable written by the mock on the server goroutine. Deliver the captured WatchSandboxRequest over a channel instead (watchRequestOrFail helper). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JkEomBo8gaAzDj3RpVFtF5 Signed-off-by: Gage Krumbach <gkrumbach@gmail.com>
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.
Summary
Relays the gateway's
WatchSandboxserver stream over a WebSocket so the sandbox detail page — draft-policy inbox and logs tab included — updates sub-second instead of waiting out the 5–10s poll intervals. Motivated by the "curl gets blocked → proposal only shows up after a manual refresh" experience: the gateway's watch bus fires on every draft mutation (including auto-approvals underproposal_approval_mode=auto), and the dashboard now listens.Backend (BFF)
gateway.WatchSandboxwrapper +watch_handler.goWS relay: resolves name → id, always follows status,?logs=trueopts into log following with the same filters as the polled logs endpoint; ping/pong keepalive detects half-dead proxy connectionsmodels.From*()(models.WatchEvent) so secret-field stripping applies — raw proto never reaches the browserFEATURE_LIVE_UPDATESis on (defaulttrue), exposed to the frontend asfeatures.liveUpdatesFrontend
useSandboxWatch: pushes sandbox snapshots straight into the React Query cache and debounces draft/policy invalidations; reconnects with capped jittered backoff and gives up after repeated failuresuseSandboxLogStream: separate socket withlogs=truefor the logs tab (tail replay + live lines, buffer capped at the selected line count, reset on reconnect); both hooks share one reconnecting-socket helperuseSandbox/useDraftPolicy/ the polled logs query suspend theirrefetchIntervalwhile a socket is live; a failed poll no longer clobbers a healthy streamDocs: ADR 0004,
FEATURE_FLAGS.md, streaming-RPC rule update.Testing
watch_e2e_test.go): real gRPC server → realgateway.Clientover TCP → real chi router → real WebSocket client; asserts snapshot and log frames relay through the whole chain, and that the route 404s when the flag is offgo test ./...,tsc --noEmit, eslint, 179 frontend testsDeployment notes
FEATURE_LIVE_UPDATES=falserestores exactly the previous polling behavior; deployments behind proxies that can't upgrade WebSockets also degrade to polling automatically after the hook's reconnect budget is exhausted.🤖 Generated with Claude Code
https://claude.ai/code/session_01JkEomBo8gaAzDj3RpVFtF5
Generated by Claude Code