templates/analytics/.agents/skills/real-time-sync/SKILL.md
How to keep the UI in sync with agent changes via polling. Use when wiring query invalidation for new data models, debugging UI not updating, or understanding jitter prevention.
npx skillsauth add BuilderIO/agent-native real-time-syncInstall 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.
The UI stays in sync with agent/script changes through database polling. When the agent writes to the database, the UI detects the change and updates automatically — no manual refresh needed.
The agent modifies data in SQL, but the UI runs in the browser. Polling bridges this gap: every database write increments a version counter, the useDbSync() hook polls for version changes, and React Query invalidates the relevant caches. This is what makes database writes feel real-time.
Server increments a version counter on every database write. The /_agent-native/poll endpoint returns the current version and any events since the last poll.
Client polls for changes and invalidates React Query caches:
import { useDbSync } from "@agent-native/core";
useDbSync({ queryClient, queryKeys: ["items", "settings"] });
When the agent writes to the database, the version increments, polling detects it, and React Query refetches the affected queries.
useDbSync() handles it (polls every 2 seconds by default)useDbSync — use the onEvent callback for custom handlingBy default, useDbSync invalidates all listed query keys on every change. For apps with multiple data models, this causes unnecessary refetches. Use event-based filtering via the onEvent callback:
useDbSync({
queryClient,
queryKeys: [], // don't auto-invalidate everything
onEvent: (data) => {
if (data.source === "settings") {
queryClient.invalidateQueries({ queryKey: ["settings"] });
} else if (data.source === "app-state") {
queryClient.invalidateQueries({ queryKey: ["navigate-command"] });
} else {
queryClient.invalidateQueries({ queryKey: ["items"] });
}
},
});
To prevent cache thrashing during rapid agent writes, set staleTime on your queries:
useQuery({
queryKey: ["items"],
queryFn: fetchItems,
staleTime: 2000, // don't refetch within 2 seconds
});
| Symptom | Check |
| ---------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| UI not updating after agent writes | Is useDbSync called with the correct queryClient? Are the queryKeys matching your useQuery keys? |
| Poll endpoint not responding | Is /_agent-native/poll accessible? Is the server running? |
| High CPU / event storms | The agent is writing rapidly. Add staleTime to queries and use event-based filtering. |
When the agent writes to application-state via script helpers (writeAppState, deleteAppState), the write is automatically tagged with requestSource: "agent". This prevents the UI from overwriting active user edits when it receives the change event.
@agent-native/core/application-state pass { requestSource: "agent" } to the store.X-Request-Source header on PUT/DELETE requests to application-state endpoints.useDbSync() accepts an ignoreSource option. The UI passes its own tab ID so it ignores events from its own writes — but still picks up events from agents, other tabs, and scripts.// app/lib/tab-id.ts
export const TAB_ID = `tab-${Math.random().toString(36).slice(2, 8)}`;
// app/root.tsx
import { TAB_ID } from "@/lib/tab-id";
useDbSync({
queryClient,
queryKeys: ["app-state", "settings"],
ignoreSource: TAB_ID,
});
The use-navigation-state.ts hook sends the same TAB_ID in the X-Request-Source header when writing navigation state, so the tab that wrote the state does not refetch it.
Without jitter prevention, a cycle occurs: the UI writes state, polling detects the change, the UI refetches and re-renders, potentially overwriting what the user is actively editing. With ignoreSource, the UI only reacts to changes from other sources (agent scripts, other browser tabs, other users).
tools
Public booking flow — the state machine, animations, and URL/app-state sync.
tools
Trigger-based automations — reminders, follow-ups, webhooks — across the booking lifecycle.
tools
Team event types, round-robin assignment, collective bookings, host weights, and no-show calibration.
development
The pure `computeAvailableSlots` function — inputs, outputs, invariants, and debugging guide.