skills/websocket-streaming/SKILL.md
--- --- name: websocket-streaming license: Apache-2.0 description: Implements real-time bidirectional communication between DAG execution engines and visualization dashboards via WebSocket. Covers connection management, typed event protocols, reconnection with backoff, and React hook integration. Activate on "WebSocket", "real-time updates", "live streaming", "execution events", "state streaming", "push notifications". NOT for HTTP REST APIs, server-sent events (SSE), or general networking. allo
npx skillsauth add curiositech/windags-skills skills/websocket-streamingInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Real-time bidirectional communication between DAG execution engines and dashboards via typed event protocols and connection state management.
CONNECTING → OPEN → CLOSING → CLOSED
↓ ↓ ↓ ↓
wait process queue reconnect
If connection === 'CONNECTING':
- Buffer outgoing messages in memory queue
- Show "connecting..." indicator
- Timeout after 10s → retry with backoff
If connection === 'OPEN':
- Send queued messages immediately
- Process incoming events via typed dispatch
- Send heartbeat every 30s
If connection === 'CLOSING':
- Stop sending new messages
- Finish processing in-flight events
- Prepare for reconnection
If connection === 'CLOSED':
- Calculate backoff: min(1000 * 2^attempt, 30000)ms
- Increment reconnect attempt counter
- Trigger reconnection after delay
If event.type === 'node_state':
- Update store.nodes[event.node_id].status
- Trigger UI re-render for affected node
- Log metrics if present
If event.type === 'cost_update':
- Update budget display in header
- Show warning if remaining < 20%
- Log cost trajectory for analytics
If event.type === 'human_gate_waiting':
- Show modal with gate presentation
- Enable approval/rejection buttons
- Start timeout countdown (default 5min)
If event.type === 'error':
- Show error toast notification
- Highlight affected node (if node_id present)
- Log to error tracking service
If connectionState === 'OPEN' && bufferQueue.length === 0:
- Send message immediately via ws.send()
If connectionState !== 'OPEN' && message.priority === 'high':
- Add to front of bufferQueue
- Limit high-priority queue to 50 messages
If connectionState !== 'OPEN' && message.priority === 'normal':
- Add to back of bufferQueue
- Drop oldest if queue > 200 messages
If connectionState becomes 'OPEN' && bufferQueue.length > 0:
- Send all queued messages in order
- Clear buffer queue
- Resume normal operation
Symptom: Rapid connect/disconnect cycles, exponentially increasing CPU usage Detection: If reconnect attempts > 5 in 60 seconds Fix: Implement exponential backoff with max delay (30s), circuit breaker after 10 failures
Symptom: Dashboard becomes unresponsive, memory usage spikes, UI freezes Detection: If incoming message rate > 100/second or buffer queue > 1000 messages Fix: Implement message throttling, batch state updates, drop non-critical events
Symptom: Dashboard shows wrong node statuses, missing execution progress Detection: If timestamp gap > 30s between disconnect and reconnect Fix: Send 'resync_request' on reconnect, server responds with full current state
Symptom: Memory usage grows steadily, never decreases, eventual crash Detection: If WebSocket reference count > 5 for single DAG, or buffer never clears Fix: Properly close previous WebSocket before creating new one, clear event listeners
Symptom: Critical events (human gates, errors) never reach dashboard Detection: If expected event doesn't arrive within timeout window Fix: Implement message acknowledgment, server retries unacknowledged critical events
Scenario: User is monitoring DAG execution. Network drops for 45 seconds during critical node processing. Connection recovers.
Step 1 - Connection Loss Detection:
// WebSocket onclose event fires
ws.onclose = () => {
setConnectionState('CLOSED');
// Decision: Network issue or server restart?
// → Try reconnect (could be temporary network)
scheduleReconnect();
};
Step 2 - Buffering Decision:
// User tries to send human decision during outage
const sendDecision = (decision) => {
if (connectionState !== 'OPEN') {
// Decision: Buffer or reject?
// → Buffer high-priority messages (human decisions)
bufferQueue.unshift({
type: 'human_decision',
priority: 'high',
timestamp: Date.now()
});
showToast("Decision queued - connection lost");
}
};
Step 3 - Reconnection with State Gap:
ws.onopen = () => {
const disconnectDuration = Date.now() - lastDisconnectTime;
if (disconnectDuration > 30000) {
// Decision: Full resync or continue?
// → 45s gap requires full resync
ws.send(JSON.stringify({ type: 'resync_request', last_seen: lastEventTimestamp }));
// Server responds with missed events + current state
// Trade-off: Higher bandwidth but guaranteed consistency
}
// Send buffered messages
flushBufferQueue();
};
What novice misses: Assumes reconnection means everything is fine, doesn't handle state gap What expert catches: Calculates disconnect duration, requests resync for gaps > 30s, preserves critical user actions in buffer
Don't use WebSocket streaming for:
polling-pattern-optimizer with REST endpoints insteadfile-transfer-handler with HTTP multipart insteadsse-event-stream for simpler one-way communicationapi-architect for traditional REST/GraphQL patternsdatabase-change-streams for direct DB subscriptionsauth-flow-manager for login/logout/token refreshDelegate to other skills:
message-queue-architectwebsocket-load-balancerwebrtc-connection-managerdata-ai
license: Apache-2.0 NOT for unrelated tasks outside this domain.
development
Use when designing caching strategies (cache-aside, write-through, write-behind), implementing distributed locks, building rate limiters, leaderboards, real-time streams (XADD/consumer groups), pub/sub, or tuning eviction policies. Triggers: thundering-herd on cache miss, dogpile on key expiry, Redlock vs SET-NX-PX choice, sliding-window rate limiter, hot-key on a single cluster slot, big-key blowup, MULTI/EXEC across slots, KEYS in production. NOT for Redis Cluster operations/admin (different domain), embedded KV (SQLite, leveldb), in-process LRU caches, or Memcached.
tools
Drawing the `'use client'` boundary correctly in React Server Components apps (Next.js App Router, RSC frameworks) — leaf-pushing, slot composition, serialization rules, and environment poisoning prevention. Grounded in react.dev and Next.js 16 docs.
development
Use when designing rate limiting for an API, choosing between token bucket / sliding window / leaky bucket / fixed window, implementing it in Redis, deciding edge (Cloudflare/Upstash) vs origin enforcement, sizing per-user vs per-IP vs per-endpoint quotas, returning the right 429 response with Retry-After, or fixing the boundary-burst bug in fixed-window limiters. Triggers: 429 too many requests, INCR + EXPIRE, ZADD + ZREMRANGEBYSCORE + ZCARD, X-RateLimit-Remaining header, Cloudflare WAF rate limiting rules, Upstash @upstash/ratelimit, leaky bucket shaping vs policing, distributed rate limiter consistency. NOT for DDoS mitigation specifically (different scale), CAPTCHA / bot management, full WAF design, or per-user quota billing.