From harness-claude
Implements XState invoke to run promises, callbacks, observables, and child machines as services on state entry/exit. For API fetches, WebSockets, timers, and sub-workflows.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Invoke promises, callbacks, observables, and child machines as services tied to state node lifecycles
Spawn and manage XState child actors for independent concurrent state machines communicating via message passing. Use for dynamic collections like file uploads, chat rooms, or player sessions.
Mandates invoking relevant skills via tools before any response in coding sessions. Covers access, priorities, and adaptations for Claude Code, Copilot CLI, Gemini CLI.
Share bugs, ideas, or general feedback.
Invoke promises, callbacks, observables, and child machines as services tied to state node lifecycles
invoke inside a state node. The service starts when the state is entered and is automatically stopped when the state is exited.onDone (success) and onError (failure) on the invoke config.event.data in onDone. The rejected value becomes event.data in onError.services (v4) or actors (v5) to keep machines testable and serializable.input (v5) or withContext (v4) to pass data to invoked machines.// data-fetch.machine.ts
import { createMachine, assign } from 'xstate';
interface FetchContext {
url: string;
data: unknown | null;
error: string | null;
}
type FetchEvent = { type: 'RETRY' } | { type: 'REFRESH' };
const fetchMachine = createMachine<FetchContext, FetchEvent>(
{
id: 'fetch',
initial: 'loading',
context: { url: '', data: null, error: null },
states: {
loading: {
invoke: {
id: 'fetchData',
src: 'fetchService',
onDone: {
target: 'success',
actions: assign({ data: (_, event) => event.data, error: null }),
},
onError: {
target: 'failure',
actions: assign({ error: (_, event) => event.data.message }),
},
},
},
success: {
on: { REFRESH: 'loading' },
},
failure: {
on: { RETRY: 'loading' },
},
},
},
{
services: {
fetchService: (ctx) =>
fetch(ctx.url).then((res) => {
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json();
}),
},
}
);
// Callback service — WebSocket connection
const chatMachine = createMachine(
{
id: 'chat',
initial: 'connected',
states: {
connected: {
invoke: {
id: 'websocket',
src: 'connectWebSocket',
},
on: {
MESSAGE_RECEIVED: { actions: 'addMessage' },
DISCONNECT: 'disconnected',
},
},
disconnected: { type: 'final' },
},
},
{
services: {
connectWebSocket: (ctx) => (sendBack, onReceive) => {
const ws = new WebSocket(ctx.wsUrl);
ws.onmessage = (msg) => sendBack({ type: 'MESSAGE_RECEIVED', data: msg.data });
// onReceive listens for events sent TO this service
onReceive((event) => {
if (event.type === 'SEND_MESSAGE') ws.send(event.text);
});
// Cleanup — called when state exits
return () => ws.close();
},
},
}
);
Service types in v4:
onDone or onError(sendBack, onReceive) => cleanup — long-lived, bidirectionalonDoneonDone fires when child reaches final stateXState v5 equivalents: Services become actors defined with helper functions:
import { fromPromise, fromCallback } from 'xstate';
const machine = setup({
actors: {
fetchService: fromPromise(async ({ input }: { input: { url: string } }) => {
const res = await fetch(input.url);
return res.json();
}),
wsService: fromCallback(({ sendBack, input }) => {
const ws = new WebSocket(input.url);
ws.onmessage = (msg) => sendBack({ type: 'MESSAGE_RECEIVED', data: msg.data });
return () => ws.close();
}),
},
}).createMachine({
/* ... */
});
Automatic cancellation: When the state that owns the invoke exits, XState automatically stops the service. For promises, the AbortController.signal is not used automatically — you must wire it yourself if needed. For callbacks, the cleanup function runs.
Multiple invocations: A state can have multiple invoke entries (use an array). All start on entry and stop on exit.
Testing: Replace services with mocks in tests by passing { services: { fetchService: mockService } } to interpret(machine.withConfig({ services: ... })).
https://stately.ai/docs/invoke