agentsclimarketplace

Real time updates

Skill jacob-balslev/skill-graph/marketplace/skills/real-time-updates

Use when designing browser-facing freshness for live dashboards, notifications, progress views, feeds, and data that can change after initial render. Covers freshness contracts, transport choice among adaptive polling, Server-Sent Events, and bidirectional sockets, webhook-to-UI propagation, client cache invalidation, stale-data indicators, reconnect and catch-up behavior, centralized subscription ownership, and non-disruptive update UX. Do NOT use for low-level stream/backpressure protocol design (use `streaming-architecture`), async event envelope/topic contracts (use `event-contract-design`), recurring schedule design (use `cron-scheduling`), durable worker execution semantics (use `background-jobs`), generic UI action feedback (use `interaction-feedback`), or serialization/trust boundaries (use `client-server-boundary`). Do NOT use for design the backpressure protocol for an HTTP stream. Do NOT use for choose the cron expression for a daily refresh.From its SKILL.md

Install
npx -y skills add jacob-balslev/skill-graph --skill real-time-updates

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its file declares

Copied from the file, not written here

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

26.8 KB, ~4.2k tokens by cl100k_base, as published. Nobody here has run it

Real-Time Updates

Concept of the skill

Browser freshness has four primitives: a source of change, a delivery channel, a browser cache or view state, and a freshness contract shown to the user.

Coverage

  • Browser freshness decisions for dashboards, feeds, progress panels, notifications, and views that can become stale after initial render.
  • Transport selection among adaptive polling, Server-Sent Events, and bidirectional sockets.
  • Webhook-to-UI propagation at a pattern level: external change, server state, browser notification, and catch-up fetch.
  • Client cache invalidation and refetch orchestration after a live notification.
  • Optimistic update safety: when to apply immediately, when to wait, and how to roll back.
  • Stale-data communication: timestamps, stale badges, connection status, and non-disruptive refresh prompts.
  • Reconnect and missed-update recovery: last seen version, cursor, timestamp, or sequence number.
  • Subscription ownership: one shared subscription per resource instead of independent polling loops in every component.

Philosophy of the skill

Stale data without a freshness signal is a false statement. A dashboard that fetched once and never says so teaches the user to trust numbers, statuses, or notifications that may already be outdated.

Real-time design is not the same as choosing the flashiest transport. Polling can be the best design when data changes slowly or hosting cannot keep long connections open. Server-Sent Events are the default for one-way server-to-browser updates. Bidirectional sockets are justified only when the browser must also send frequent low-latency messages over the same channel. The transport is a consequence of the freshness contract, not the starting point.

The strongest systems also keep control with the user. If an update would reorder a table, interrupt a form, clear a selection, or move the scroll position, the interface should announce that new data is available instead of silently replacing the view.

A browser update message should usually be treated as a freshness signal, not as the entire source of truth. Push can tell the browser what changed; the browser still reconciles from durable server state or a cache that is invalidated and refetched.

Freshness Contract First

Before choosing a transport, define the user-visible freshness contract.

QuestionDesign implication
How stale can the view be before it misleads?Defines polling interval, staleness threshold, and warning copy.
Can updates apply while the user is interacting?If no, show a "new data available" prompt instead of auto-refreshing.
Can the browser miss events during disconnect?Requires catch-up by version, cursor, timestamp, or full refetch.
Is every event important, or only latest state?Event streams need replay; latest-state views can refetch on reconnect.
Does the browser need to send frequent messages back?If yes, consider bidirectional sockets; if no, prefer SSE or polling.
Must the state survive page refresh?Store durable state on the server and treat browser state as a cache.

Transport Decision Matrix

RequirementAdaptive pollingServer-Sent EventsBidirectional socket
Server-to-browser updatesYes, delayed by intervalYes, near-real-timeYes
Browser-to-server messagesSeparate HTTP requestSeparate HTTP requestSame connection
Reconnect behaviorEach request is independentBuilt into EventSourceMust be implemented
Hosting friendlinessHighestDepends on connection duration limitsRequires a persistent socket-capable runtime
Proxy and CDN compatibilityHighestGood with streaming supportMore fragile
Binary payloadsNo special supportText events onlySupported
Best fitLow or moderate freshness needsOne-way dashboards, progress, feeds, notificationsCollaboration, presence, live cursors, games, control channels

Decision Rule

Use this order:

  1. If the browser only needs periodic latest state and seconds of delay are acceptable, use adaptive polling.
  2. If the server needs to push one-way updates and long connections are supported, use Server-Sent Events.
  3. If the browser must send frequent low-latency messages over the same live channel, use a bidirectional socket.
  4. If the selected transport cannot replay or recover missed updates, add a catch-up fetch after reconnect.

Do not choose a bidirectional socket only because the feature is called "real-time." Directionality and recovery requirements decide.

Webhook-To-UI Propagation

An external change does not automatically become a browser update. Treat it as a four-stage pipeline.

External change
  -> Server receives and validates the change
  -> Durable state is updated
  -> Browser freshness channel is notified or polled
  -> Browser reconciles from authoritative state

The browser should usually reconcile from authoritative server state rather than trusting the push message as the full truth. A push message can be small: resource type, resource ID, version, and timestamp. The browser then decides whether to refetch a row, a page, or a summary.

Propagation patternUse whenWatch out for
Poll latest versionLow frequency, simple hosting, broad compatibilityLatency equals interval; many components can duplicate work
Push invalidationBrowser can refetch changed stateRequires versioning or timestamps for missed updates
Push full patchPatch is small and ordering is reliableMore complex conflict handling and replay semantics
Push progress stateLong work has durable progress fieldsBackground job state still needs its own reliable contract

Adaptive Polling

Polling is not a failure. Poor polling is the failure.

Use adaptive polling when persistent connections are unnecessary or unavailable:

  • Poll faster while the tab is visible and the view is active.
  • Poll slower or pause when the tab is hidden.
  • Use conditional requests, versions, cursors, or updated-at filters when available.
  • Centralize polling per resource so multiple components share one result.
  • Surface stale state when polling fails or data ages beyond the threshold.
type PollingState<T> = {
  data: T | null;
  lastFetchedAt: number | null;
  isStale: boolean;
  error: Error | null;
};

function choosePollInterval(isVisible: boolean, hasRecentActivity: boolean) {
  if (!isVisible) return 60_000;
  if (hasRecentActivity) return 5_000;
  return 20_000;
}

Avoid putting document.hidden directly in a React dependency array. Track visibility through a visibilitychange listener or a shared visibility hook, then derive the interval from that state.

Server-Sent Events

Use SSE for one-way server-to-browser updates such as status feeds, progress, notification counts, or dashboard invalidation messages.

Minimum SSE contract:

  • Event type: what kind of update this is.
  • Event ID or version: what the browser can use to resume or catch up.
  • Data payload: small enough to process quickly.
  • Heartbeat or keepalive: keeps intermediaries from closing quiet streams.
  • Abort cleanup: closes timers, subscriptions, and handles when the browser disconnects.
export async function GET(request: Request) {
  const encoder = new TextEncoder();

  const stream = new ReadableStream({
    start(controller) {
      const heartbeat = setInterval(() => {
        controller.enqueue(encoder.encode(': keepalive\n\n'));
      }, 25_000);

      request.signal.addEventListener('abort', () => {
        clearInterval(heartbeat);
        controller.close();
      });
    },
  });

  return new Response(stream, {
    headers: {
      'Content-Type': 'text/event-stream',
      'Cache-Control': 'no-store',
    },
  });
}

SSE has built-in reconnect, but that does not guarantee the browser received every update. Use Last-Event-ID, a version field, or a timestamp to fetch missed state after reconnect.

On the browser side, keep the live stream small and treat it as an invalidation signal. Store the last event ID, show connection state, and reconcile from durable server state after reconnect.

let lastEventId: string | null = null;

function subscribeToOrderUpdates(refetchSince: (id: string | null) => Promise<void>) {
  const events = new EventSource('/api/order-events');

  events.onopen = async () => {
    setConnectionState('live');
    await refetchSince(lastEventId);
  };

  events.addEventListener('order.updated', async (event) => {
    lastEventId = event.lastEventId || JSON.parse(event.data).version;
    invalidateOrderCache(JSON.parse(event.data).orderId);
  });

  events.onerror = () => {
    setConnectionState('reconnecting');
  };

  return () => events.close();
}

If the server cannot replay from Last-Event-ID, use the remembered event ID, version, cursor, or timestamp as a catch-up query parameter on reconnect. If even that is unavailable, perform a full refetch before marking the view live again.

Bidirectional Sockets

Use a bidirectional socket only when the browser must send frequent low-latency messages while also receiving server updates.

Good fits:

  • Presence and live cursors.
  • Collaborative editing.
  • Multiplayer or live control surfaces.
  • Chat-like interactions where both sides can speak at any time.

Poor fits:

  • Notification badges.
  • Dashboard invalidation.
  • Progress updates.
  • Occasional browser actions that can use ordinary HTTP requests.

If a bidirectional socket is justified, verify heartbeat, reconnect, backoff, authorization, room cleanup, message envelope, and backpressure with streaming-architecture or a dedicated socket skill in the consuming environment.

Optimistic Updates

Optimistic updates make the interface feel instant, but they are safe only when the action is reversible and the speculative state cannot mislead.

ScenarioUse optimistic UI?Reason
Toggle a preferenceYesReversible and low risk.
Mark an item readYesClear intent and easy rollback.
Inline text editSometimesUse local draft state until save succeeds if conflicts are likely.
Delete a recordUsually noRemoval is disruptive if rollback is needed.
Financial, legal, or irreversible actionNoSpeculative state can mislead or imply completion.

Every optimistic update needs:

  • Previous state for rollback.
  • Pending affordance so the user knows confirmation is not final yet.
  • Failure message with recovery.
  • Reconciliation with server state after success.

Stale Data Indicators

Every live view should communicate freshness at the level of precision the user needs.

PatternUse whenExample
Relative timestampData can age but remains useful"Updated 2 minutes ago"
Connection badgeStream health matters"Live", "Reconnecting", "Offline"
Staleness warningData age can mislead decisions"Data may be outdated"
New-data bannerAuto-refresh would disrupt work"New data available. Refresh"
Disabled live claimFreshness is unknownShow timestamp, not "live"

Auto-refresh is safe for small, non-interactive counters. It is not safe for tables, forms, selected lists, or scroll-sensitive surfaces unless the update preserves the user's position and selection.

Reconnect And Catch-Up

Persistent connections drop during deploys, network changes, sleep/wake cycles, and proxy timeouts. Reconnect is incomplete without catch-up.

Use this recovery sequence:

  1. Detect disconnect and show a non-blocking connection status.
  2. Reconnect with exponential backoff and jitter.
  3. Send or remember the last seen event ID, version, cursor, or timestamp.
  4. Fetch missed state before declaring the view current.
  5. Deduplicate updates that arrive both through catch-up and the live channel.
  6. Clear the warning only after authoritative state has been reconciled.

If the system cannot determine what was missed, perform a full refetch and say the view was refreshed, not replayed.

Subscription Ownership

Do not let every component open its own connection or polling loop for the same resource.

Better ownership patterns:

  • One route-level subscription that distributes state through context or a shared store.
  • One query-cache subscription that invalidates resource keys.
  • One server stream per browser view, multiplexing update types in the event payload.
  • One polling coordinator that batches interested components.

Central ownership reduces duplicate requests, connection count, inconsistent stale indicators, and race conditions between components.

Verification

After applying this skill, verify:

  • The freshness contract is explicit: acceptable age, user disruption rules, and recovery behavior are named.
  • The transport choice matches directionality, hosting limits, and recovery needs.
  • Server-to-browser-only flows do not default to bidirectional sockets without a specific bidirectional requirement.
  • Persistent connections have reconnect, backoff, visible status, cleanup on disconnect, and catch-up from last seen state.
  • Polling adapts to visibility or activity and is centralized per shared resource.
  • Every stale or live claim has a timestamp, status badge, or equivalent freshness indicator.
  • Optimistic updates include pending state, rollback, failure recovery, and server reconciliation.
  • Auto-refresh does not disrupt active typing, selection, scrolling, or filtering.
  • Missed updates are deduplicated after reconnect or full refetch.

Do NOT Use When

Use insteadWhen
streaming-architectureYou are designing low-level producer/consumer/backpressure/termination semantics, HTTP streaming internals, or stream error encoding.
background-jobsYou are moving slow work into a durable worker, designing job states, retries, progress records, or cancellation.
cron-schedulingYou are choosing when recurring work starts, preventing schedule overlap, or designing missed-run behavior.
event-contract-designYou are defining async event envelopes, topic names, compatibility, replay, or producer/consumer ownership.
interaction-feedbackYou are designing generic loading, pending, error, retry, undo, or success feedback for an action without a browser freshness or missed-update problem.
client-server-boundaryYou are deciding what can cross between server and client runtimes, how it serializes, or which side can be trusted.
debuggingA live update channel is already failing and you need reproduction, logs, and root-cause isolation.

Anti-Patterns

Anti-patternWhy it failsBetter pattern
Calling static data "live"The user trusts freshness that the system cannot proveShow timestamp or connection status
Bidirectional socket for one-way updatesAdds operational complexity without product valueUse SSE or adaptive polling
Fixed polling in every componentWastes requests and creates inconsistent stateCentralize polling or use a shared cache
Reconnect without catch-upThe connection resumes but missed updates vanishFetch by last seen ID, version, cursor, timestamp, or full refetch
Silent auto-refresh of interactive viewsCauses layout shifts and lost contextShow a new-data banner or preserve position and selection
Optimistic UI for irreversible actionsRollback is confusing or misleadingWait for confirmation and show progress
No visible stale state after failuresThe user cannot distinguish old data from current dataShow stale warning, retry state, or last successful fetch time

Skill Graph context

<!-- skill-graph-context:start (generated — do not edit by hand) -->

Classification

  • Subject: backend-engineering
  • Public: true
  • Domain: engineering/realtime/browser-freshness
  • Scope: Designing browser-facing freshness for web application views whose data can change after initial render: freshness contracts, stale-state communication, adaptive polling, Server-Sent Events, bidirectional socket justification, webhook-to-UI propagation, reconnect/catch-up, client cache invalidation, optimistic update reconciliation, and centralized subscription ownership. Portable across dashboards, feeds, progress panels, notifications, and live status surfaces. Excludes low-level streaming/backpressure contracts (streaming-architecture), async event envelope/topic contracts (event-contract-design), recurring trigger design (cron-scheduling), durable worker execution semantics (background-jobs), generic UI action feedback states (interaction-feedback), and server/client serialization or trust boundaries (client-server-boundary).

When to use

  • choose between polling, SSE, and WebSocket for a live dashboard
  • show stale data status and last-updated timestamp for a live dashboard without disrupting the user
  • design reconnect and catch-up behavior after an EventSource disconnect
  • add optimistic UI with rollback and server reconciliation for a live list update
  • centralize polling and subscription ownership so dashboard widgets share one freshness channel
  • Triggers: real-time-updates-skill, live-data-skill, browser-freshness-skill, dashboard-refresh-skill, stale-data-skill

Not for

  • design the backpressure protocol for an HTTP stream
  • choose the cron expression for a daily refresh
  • move a slow export into a queue and define retry policy
  • debug a deployed stream outage

Related skills

  • Verify with: event-contract-design, interaction-feedback, client-server-boundary, streaming-architecture
  • Related: streaming-architecture, background-jobs, cron-scheduling, event-contract-design, interaction-feedback, client-server-boundary

Concept

  • Mental model: Browser freshness has four primitives: a source of change, a delivery channel, a browser cache or view state, and a freshness contract shown to the user. The delivery channel can be polling, Server-Sent Events, or a bidirectional socket, but the user contract is the same: communicate what changed, how fresh the view is, whether the connection is healthy, and how missed changes are recovered.
  • Purpose: Real-time update design prevents a rendered view from pretending old data is current. It replaces page-load-only fetching and disruptive blind auto-refresh with explicit freshness indicators, centralized subscriptions, reconnect catch-up, and transport choices matched to directionality and infrastructure constraints.
  • Boundary: This skill is not low-level streaming protocol design, async event contract design, worker queue design, schedule design, generic interaction feedback, client/server serialization and trust analysis, or incident debugging. It starts when a browser-facing view needs to stay fresh and ends with the freshness contract, transport choice, subscription ownership, stale-state UX, reconnect recovery, cache invalidation, and optimistic-update reconciliation.
  • Analogy: A real-time UI is a newsroom ticker: it needs a wire service, an editor that knows what changed, and a visible timestamp so readers know whether the headline is current.
  • Common misconception: The common mistake is treating real-time as a transport choice first. The actual design starts with the freshness promise: how current the view must be, what happens during disconnect, when updates are safe to apply automatically, and when the user needs control.

Grounding

  • Mode: universal
  • Truth sources: https://html.spec.whatwg.org/multipage/server-sent-events.html, https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events, https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API, https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API, https://www.rfc-editor.org/rfc/rfc6455, https://react.dev/reference/react/useOptimistic, https://tanstack.com/query/latest/docs/framework/react/guides/polling, ../skills/skills/backend-engineering/real-time-updates/references/real-time-updates-2026-06-07.md

Keywords

  • real-time updates, live dashboard, browser freshness, stale data indicator, Server-Sent Events, WebSocket, adaptive polling, reconnect catch-up, client cache invalidation, freshness timestamp
<!-- skill-graph-context:end -->

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 326,629. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.