add-a-lifecycle-machine
Learn how XState owns a lifecycle while Effect owns its work, errors, and services.
Skill by joelhooks
Install
Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.
npx agentmarket install skill/joelhooks-add-a-lifecycle-machine-6b3726
Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.
Get the weekly diff
The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.
About
Reviews
Write a reviewNo reviews yet. Installed it? Reviews come from verified installers only. How reviews work
Permissions
What this skill can do and what it asks for, from its SKILL.md and code. Checked Oct 25, 2026.
- Tools
- No tools found
- Secrets
- None asked for
- Install scripts
- None found
Network hosts it talks to 1
codeload.github.com· found in install command
Files it touches
~/.claude/skills/add-a-lifecycle-machine
What's inside
- Skill name
add-a-lifecycle-machine- Description
- Learn how XState owns a lifecycle while Effect owns its work, errors, and services.
- License
- MIT
- Instructions
- 145 lines in SKILL.md · view on GitHub
Files (2)
- SKILL.md
- lifecycle.model.test.md · templates/
SKILL.md (first lines)
# Add a lifecycle machine
A lifecycle machine defines named states and the events that permit movement between them. Effect describes work with typed failures and required dependencies. A service is a named interface for one job. Schema defines accepted values and their runtime checks.
Copy `packages/core/src/inspect-machine.ts` when the work has states that matter. Keep a direct Effect when it does not.
A machine that calls a provider also follows [durability](/skills/durability): one timeout, bounded retries with the same key, and nothing held across the call.
Before changing Effect or XState code, read `node_modules/effect/AGENTS.md`. Then read the pinned XState and `@xstate/effect` source listed in `AGENTS.md`.
## 1. Write the states first
List the states and final results before writing the machine.
For file inspection, the states are `reading`, `inspected`, and `unreadable`. The final result is either `Inspected` with file stats or `Unreadable` with a `FileStatsError`.
Use an explicit result union when a known error should move the machine into a final state.
## 2. Put side effects in actors
Define each side effect with `createEffectLogic` outside the machine:
```ts
const performWork = createEffectLogic({
effect: ({ input }) => ThingService.use((service) => service.run(input.id)),
schemas: { input: Schema.Struct({ id: Schema.String }) },
});
```
An actor is a running unit that processes events or performs work for a machine. These actors carry typed errors and service dependencies. Declare them through `setupEffect`. Do not return an Effect from an inline XState callback.
## 3. Build the machine
Pass the actors and schemas to `setupEffect`, then call `createMachine`:
```ts
type ThingOutcome =
| { readonly _tag: "Succeeded"; readonly value: ThingResult }
| { readonly _tag: "Failed"; readonly error: ThingError };
interface ThingContext {
readonly id: string;
readonly outcome: ThingOutcome | undefined;
}
export const thingMachine = setupEffect({
actors: { performWork },
schemas: {
context: types<ThingContext>(),
input: Schema.Struct({ id: Schema.String }),
},
}).createMachine({
context: ({ input }) => ({ id: input.id, outcome: undefined }),
initial: "working",
output: ({ context }) => context.outcome,
states: {
working: {
invoke: {
src: "performWork",
input: ({ context }) => ({ id: context.id }),
onDone: {
target: "succeeded",
context: ({ context, event }) => ({
...context,
outcome: { _tag: "Succeeded", value: event.output },
}),
},
onError: {
target: "failed",
context: ({ context, event }) => ({
...context,
outcome: { _tag: "Failed", error: event.error },
}),
},
},
},
succeeded: { type: "final" },
failed: { type: "final" },
},
});
```
XState owns states and moves between them. Effect owns side effects, errors, services, and cleanup.
## 4. Run it inside Effect
Start the machine with `createEffectActor`. Do not use XState's `createActor`. Hand the actor to `watchActor` from `@rat-stack/capability/actor-watch`, then wait for it with `join` inside `Effect.scoped`:
```ts
export const runThingMachine = Effect.fn("runThingMachine")(function* (
id: string
) {
const actor = yield* createEffectActor(thingMachine, { input: { id } });
yield* watchActor("thingMachine", actor);
// @effect-diagnostics-next-line anyUnknownInErrorContext:off -- join exposes unknown machine failures; unexpected failures become defects with orDie.
const outcome = yield* join(actor).pipe(Effect.orDie);
if (outcome === undefined) {
return yield* Effect.die(new Error("machine completed without an outcome"));
}
if (outcome._tag === "Failed") {
return yield* outcome.error;
}
return outcome.value;
}, Effect.scoped);
```
`watchActor` does nothing until devtools runs the capability. Then `rat_list_actors`, `rat_list_transitions`, and `rat_get_actor` show every state and event. `rat-stack-patterns/watch-effect-actors` fails lint when a runtime module starts an actor without it.
A missing result or machine-level error is a bug in this design. A known product error belongs in the final result. `join` has an `unknown` machine-error channel, so the example uses one narrow diagnostic override before `Effect.orDie`.
Call the runner from the capability handler, the server-side implementation of one named action. Provide its service Layer in `apps/cli/src/cli.ts`. A Layer builds services and supplies their construction dependencies.
## 5. Generate inputs and command histories
Add a property and a model test with each lifecycle. Use `effect/Arbitrary` and `@effect/vitest`. Do not add a separate generator dependency.
Copy the complete test template at `skills/add-a-lifecycle-machine/templates/lifecycle.model.test.md` into `packages/core/test/<name>.model.test.ts`. It runs unchanged for `inspectMachine`. Replace its schemas, service, outcomes, and commands for your lifecycle. The executable reference is `packages/core/test/inspect-machine.model.test.ts`.
- Derive generated inputs, outputs, and failures from the domain schemas.
- Keep the model independent: pending until work settles, then one immutable terminal outcome.From joelhooks/rat-stack, MIT.
More from joelhooks/rat-stack
- 0.684score
rat-stack-mode
joelhooks
Route non-trivial rat-stack code, data, design, investigation, and documentation tasks to every matching playbook and principle.
SkillAFreshness: A★ 129Claude Code +9 - 0.682score
profile-a-worker
joelhooks
Capture a CPU or heap profile of a deployed Cloudflare Worker or Durable Object and read it with pprof.
SkillAFreshness: A★ 129Claude Code +9 - 0.682score
investigate-a-failure
joelhooks
Find the cause of a crash, wrong output, flaky test, or slow run with evidence, fix it at its source, and leave a notebook a person can learn from.
SkillAFreshness: A★ 129Claude Code +9 - 0.682score
uncomplect
joelhooks
Find responsibilities that a rat-stack design combines, separate them into capabilities, cartridges, machines, features, and clients, and enforce that…
SkillAFreshness: A★ 129Claude Code +9 - 0.682score
query-design
joelhooks
Design or review SQL reads, writes, scans, backfills, and slow database paths.
SkillAFreshness: A★ 129Claude Code +9 - 0.681score
ship
joelhooks
Learn how to ship a change through merge, CI, release, stage deployment, rollout, verification, rollback and flags.
SkillAFreshness: A★ 129Claude Code +9 - 0.679score
find-peers
joelhooks
Refresh public peers, exact stack versions, and usefulness tiers every week or two and after each shared dependency update.
SkillAFreshness: A★ 129Claude Code +9 - 0.679score
learn
joelhooks
Present Effect, Alchemy, XState, and application-design concepts during real work when the operator enables learn mode.
SkillAFreshness: A★ 129Claude Code +9 - 0.679score
gardener
joelhooks
Keep rat-stack current and clean.
SkillAFreshness: A★ 129Claude Code +9 - 0.679score
add-a-store
joelhooks
Add persistence through a service named for an application job, with validation, vendor Layers, migrations, bounded reads, and shared backend tests.
SkillAFreshness: A★ 129Claude Code +9 - 0.676score
add-a-capability
joelhooks
Learn how one contract and its handler become a command, HTTP route, MCP tool, browser RPC, and sandbox call.
SkillAFreshness: A★ 129Claude Code +9 - 0.676score
learn-alchemy
joelhooks
Learn how Alchemy 2 turns an Effect program into a planned Cloudflare deployment.
SkillAFreshness: A★ 129Claude Code +9
Signals
- Stars
- 129
- Forks
- 8
- Open issues
- 2
- Last commit (this folder)
- Oct 18, 2026 (11 days ago)
From GitHub (fetched Oct 24, 2026).
Details
- License
- MIT
- Repository
- github.com/joelhooks/rat-stack
- First seen
- Oct 24, 2026
- Last checked
- Oct 24, 2026
- Source
- GitHubgithub-graphql
Same skill elsewhere
Copies of this SKILL.md published by others; search shows only one of them.
- 0.337score
add-a-lifecycle-machine
joelhooks
Learn how XState owns a lifecycle while Effect owns its work, errors, and services.
SkillAFreshness: A★ 27Claude Code +9 - 0.335score
add-a-lifecycle-machine
ChindanaiNaKub
Learn how XState owns a lifecycle while Effect owns its work, errors, and services.
SkillAFreshness: AClaude Code +9 - 0.323score
add-a-lifecycle-machine
joelhooks
Learn how XState owns a lifecycle while Effect owns its work, errors, and services.
SkillAFreshness: A★ 6Claude Code +9
More from joelhooks
- 0.684score
rat-stack-mode
joelhooks
Route non-trivial rat-stack code, data, design, investigation, and documentation tasks to every matching playbook and principle.
SkillAFreshness: A★ 129Claude Code +9 - 0.682score
uncomplect
joelhooks
Find responsibilities that a rat-stack design combines, separate them into capabilities, cartridges, machines, features, and clients, and enforce that…
SkillAFreshness: A★ 129Claude Code +9 - 0.682score
investigate-a-failure
joelhooks
Find the cause of a crash, wrong output, flaky test, or slow run with evidence, fix it at its source, and leave a notebook a person can learn from.
SkillAFreshness: A★ 129Claude Code +9 - 0.682score
profile-a-worker
joelhooks
Capture a CPU or heap profile of a deployed Cloudflare Worker or Durable Object and read it with pprof.
SkillAFreshness: A★ 129Claude Code +9 - 0.682score
query-design
joelhooks
Design or review SQL reads, writes, scans, backfills, and slow database paths.
SkillAFreshness: A★ 129Claude Code +9 - 0.681score
ship
joelhooks
Learn how to ship a change through merge, CI, release, stage deployment, rollout, verification, rollback and flags.
SkillAFreshness: A★ 129Claude Code +9
Cite this page
add-a-lifecycle-machine skill. Agent Mart. https://agentmart.dev/skills/joelhooks-add-a-lifecycle-machine-6b3726 (last updated 2026-10-24)