From harness-claude
Defines XState state machines with createMachine for explicit states, transitions, context, and events. For modeling complex UI flows like forms, wizards, authentication, and workflows preventing illegal transitions.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Define statecharts with createMachine for explicit states, transitions, context, and events
Generates full TypeScript type safety for XState machines using typegen (v4) and setup pattern (v5). Provides autocompletion for states/events/actions; resolves errors in guards/actions.
Models complex UI flows as finite state machines with states, events, transitions, actions, and guards. Useful for forms, data fetching, authentication flows, and wizards.
Designs finite state machines and statecharts for modeling entity lifecycles, workflows, and system behaviors using Harel semantics, PlantUML, and Mermaid notation.
Share bugs, ideas, or general feedback.
Define statecharts with createMachine for explicit states, transitions, context, and events
isLoading && !isError && isSubmitted) with named states.machine.ts file. Keep it separate from UI components.Context (extended state data) and Events (a discriminated union of all possible events).createMachine with id, initial, context, and states. Each state has on for transitions.idle, loading, error), not verbs.SUBMITTED, LOADED, ERRORED) or imperative commands (SUBMIT, RETRY).target for the destination state. Omit target for self-transitions that only run actions.// auth.machine.ts
import { createMachine, assign } from 'xstate';
interface AuthContext {
user: { id: string; name: string } | null;
error: string | null;
retries: number;
}
type AuthEvent =
| { type: 'LOGIN'; email: string; password: string }
| { type: 'LOGOUT' }
| { type: 'RETRY' }
| { type: 'done.invoke.authenticate'; data: { id: string; name: string } }
| { type: 'error.platform.authenticate'; data: Error };
const authMachine = createMachine<AuthContext, AuthEvent>({
id: 'auth',
initial: 'idle',
context: {
user: null,
error: null,
retries: 0,
},
states: {
idle: {
on: {
LOGIN: 'authenticating',
},
},
authenticating: {
invoke: {
id: 'authenticate',
src: 'authenticateUser',
onDone: {
target: 'authenticated',
actions: assign({ user: (_, event) => event.data, error: null }),
},
onError: {
target: 'error',
actions: assign({
error: (_, event) => event.data.message,
retries: (ctx) => ctx.retries + 1,
}),
},
},
},
authenticated: {
on: {
LOGOUT: {
target: 'idle',
actions: assign({ user: null }),
},
},
},
error: {
on: {
RETRY: {
target: 'authenticating',
guard: 'canRetry',
},
LOGIN: 'authenticating',
},
},
},
});
export { authMachine };
Context vs state: "State" in XState means the finite state node (idle, loading, error). "Context" is the extended state — arbitrary data that travels with the machine. Use finite states for mode/phase, context for data.
XState v5 changes: If using XState v5, the API shifts to setup().createMachine() with a different structure:
import { setup, assign } from 'xstate';
const machine = setup({
types: {} as {
context: AuthContext;
events: AuthEvent;
},
guards: { canRetry: ({ context }) => context.retries < 3 },
actions: { clearUser: assign({ user: null }) },
}).createMachine({
id: 'auth',
initial: 'idle',
context: { user: null, error: null, retries: 0 },
states: {
/* ... */
},
});
State node types:
atomic — leaf node, no child states (default)compound — has child states (nested)parallel — all child states active simultaneouslyfinal — terminal state, triggers onDone in the parenthistory — remembers the last active child stateDesign principles:
https://stately.ai/docs/machines