# Lifecycles are machines

> For agents: start with the [agent guide](https://ratstack.sh/llms.txt). Every page is Markdown by default; add `Accept: text/html` for HTML.

New on /news: [tla-ts 0.0.9 adds TLC's -continue](/news).

**Goal:** move a lifecycle out of boolean flags and into named states, with side effects in declared actors.

A machine is a definition of named states and the events that move between them. Use an XState machine for the application's named states, retries, cancellation, or a lifecycle you need to resume or inspect.
Model the states and the events that move between them instead of accumulating booleans whose combinations have no meaning.

An actor is a running unit that performs declared work.
A side effect is work outside the state description, such as reading a file.
A [Scope](/lore/scopes-own-resources) owns cleanup when that work ends.

## Worked example: inspecting a file

`packages/core/src/inspect-machine.ts` is the working example.
It reads a file and ends in one of two final states.

### 1. Declare the side effect as an actor

packages/core/src/inspect-machine.ts at a3f5a14861ce89913e82bea0f7ba02c83e8d4725; lines 27-31; highlighted none. Title: readStats is the only code that touches the file.

```typescript
const readStats = createEffectLogic({
  effect: ({ input }) =>
    FileInspector.use((inspector) => inspector.inspect(input.path)),
  schemas: { input: Schema.Struct({ path: Schema.String }) },
});
```

`createEffectLogic` defines an Effect-backed side effect with an input [Schema](/lore/schemas-define-the-boundary).

### 2. Let the machine choose transitions

packages/core/src/inspect-machine.ts at a3f5a14861ce89913e82bea0f7ba02c83e8d4725; lines 41-64; highlighted 45-47,52,58. Title: reading invokes readStats, then lands in a final state.

```typescript
  initial: "reading",
  output: ({ context }) => context.outcome,
  states: {
    inspected: { type: "final" },
    reading: {
      invoke: {
        input: ({ context }) => ({ path: context.path }),
        onDone: {
          context: ({ event }) => ({
            outcome: inspectOutcome.Inspected({ stats: event.output }),
          }),
          target: "inspected",
        },
        onError: {
          context: ({ event }) => ({
            outcome: inspectOutcome.Unreadable({ cause: event.error.cause }),
          }),
          target: "unreadable",
        },
        src: "readStats",
      },
    },
    unreadable: { type: "final" },
  },
```

`setupEffect` declares actors. The machine invokes `readStats` by name in the `reading` state.
The machine chooses transitions and records its outcome. The invoked actor performs the file read.

A read failure lands in `unreadable` with its cause. The failure is a state you can test.

### 3. Start it inside Effect and wait for the outcome

packages/core/src/inspect-machine.ts at a3f5a14861ce89913e82bea0f7ba02c83e8d4725; lines 67-70; highlighted 69-70. Title: createEffectActor starts it; watchActor reports it.

```typescript
export const runInspectMachine = Effect.fn("runInspectMachine")(
  function* runInspectMachine(path: string) {
    const actor = yield* createEffectActor(inspectMachine, { input: { path } });
    yield* watchActor("inspectMachine", actor);
```

`createEffectActor` starts the machine inside Effect's scope. `watchActor` makes the run visible to devtools.
`join` waits for the final outcome. `Effect.orDie` treats a machine-level failure as a programming error. The domain read failure remains an explicit machine state.

## Why a machine

Two booleans, `loading` and `failed`, allow four combinations. Only three of them mean anything here.
This machine has exactly three states: `reading`, `inspected`, and `unreadable`.

The [fence](/lore/the-fence) keeps side effects in declared actors. `oxlint.config.ts` loads the lint plugin that `@xstate/effect` ships and sets `xstate-effect/no-inline-effect` to `error`.

That rule matches two cases. It flags an Effect returned from an inline `enq` or `enqueue` callback. It flags inline Effect logic passed to `enq.spawn`. [Rule source](https://unpkg.com/@xstate/effect@1.0.0-beta.0/dist/xstate-effect-oxlint.js)

The stack pins XState `6.0.0-beta.1` and `@xstate/effect` `1.0.0-beta.0` together.
The [migration note](https://github.com/joelhooks/rat-stack/blob/main/.brain/archives/resources/xstate-alpha69-migration.svx#beta1-and-bridge-beta0) records the beta API changes and persistence audit.

## Next

Use [the add-a-lifecycle-machine skill](/skills/add-a-lifecycle-machine) to build one.

See [one capability, every surface](/lore/one-capability-every-surface) for how a capability can own a machine and [the fence](/lore/the-fence) for the rule that protects it.

A machine in memory ends with its process. [Restart must reconstruct known state](/lore/restart-from-known-state) and [supervision chooses what restarts](/lore/supervision-is-a-recovery-policy) cover recovery.

## Sources

1. [@xstate/effect 1.0.0-beta.0 oxlint plugin](https://unpkg.com/@xstate/effect@1.0.0-beta.0/dist/xstate-effect-oxlint.js)
   unpkg. no-inline-effect reports Effects returned from inline enq callbacks and inline Effect logic passed to enq.spawn. Accessed 2026-11-08.

2. [rat-stack/AGENTS.md at main · joelhooks/rat-stack · GitHub](https://github.com/joelhooks/rat-stack/blob/main/AGENTS.md)
   GitHub joelhooks/rat-stack. Lifecycle rule; used for declared Effect actors and explicit states. Accessed 2026-10-01.

3. [rat-stack oxlint configuration](https://github.com/joelhooks/rat-stack/blob/a3f5a14861ce89913e82bea0f7ba02c83e8d4725/oxlint.config.ts)
   GitHub joelhooks/rat-stack. Loads the @xstate/effect/oxlint plugin and sets xstate-effect/no-inline-effect to error. Accessed 2026-11-08.

4. [Inspection machine at a3f5a14](https://github.com/joelhooks/rat-stack/blob/a3f5a14861ce89913e82bea0f7ba02c83e8d4725/packages/core/src/inspect-machine.ts)
   GitHub joelhooks/rat-stack. Pinned source for the excerpts on this page. Accessed 2026-11-08.

5. [rat-stack/packages/core/src/inspect-machine.ts at main · joelhooks/rat-stack · GitHub](https://github.com/joelhooks/rat-stack/blob/main/packages/core/src/inspect-machine.ts)
   GitHub joelhooks/rat-stack. Inspection machine; used as the working actor and scope example. Accessed 2026-10-01.

6. [rat-stack/packages/core/test/inspect-machine.test.ts at main · joelhooks/rat-stack · GitHub](https://github.com/joelhooks/rat-stack/blob/main/packages/core/test/inspect-machine.test.ts)
   GitHub joelhooks/rat-stack. Machine tests; used for the inspection lifecycle's behavior. Accessed 2026-10-01.

## Lore on this page

- [Scopes release resources when work ends](/lore/scopes-own-resources)

## Linked from

- add-a-capability → Learn how one contract and its handler become a command, HTTP route, MCP tool, browser RPC, and sandbox call. → [Read page](https://ratstack.sh/skills/add-a-capability)

- add-a-lifecycle-machine → Learn how XState owns a lifecycle while Effect owns its work, errors, and services. → [Read page](https://ratstack.sh/skills/add-a-lifecycle-machine)

- Agent guide → An Effect stack so pure (aspirational) Kit Langton will blush. → [Read page](https://ratstack.sh/llms.txt)

- Agent rules for rat-stack (AGENTS.md) → What you may change, which commands to run, and which changes need approval. → [Read page](https://ratstack.sh/AGENTS.md)

- Build and run rat-stack (README.md) → What is in the repo, how the example works, and how to run it. → [Read page](https://ratstack.sh/README.md)

- Change log → What changed in the files served here, newest first. → [Read page](https://ratstack.sh/log)

- Circuit breakers have state → A breaker stops doomed calls and controls probes before restoring traffic. → [Read page](https://ratstack.sh/lore/circuit-breakers-have-state)

- Code snippets quote a pinned source → Shipped design brief: pinned Git objects, typed diagnostics, and one build-only snippet model for both views. → [Read page](https://ratstack.sh/lore/code-snippets)

- Concurrent work needs an owner and a bound → Limit active work, retain result order, and stop child work when its owning lifetime ends. → [Read page](https://ratstack.sh/lore/concurrent-work-needs-an-owner)

- Context and requirements → Effect requirements make a computation's needed services visible in its type. → [Read page](https://ratstack.sh/lore/context-and-requirements)

- Durability → Seven durable Effect patterns, each with its seam, its fence, and the incident that taught it. → [Read page](https://ratstack.sh/systems/durability)

- Durability starts with a recovery record → A reading order for storage, replay, worker ownership, overload, and recovery tests. → [Read page](https://ratstack.sh/lore/durability)

- Durable timers need recovery → Persist scheduled intent when losing a worker must not lose its deadlines. → [Read page](https://ratstack.sh/lore/durable-timers-need-recovery)

- Effect 4 study: September 2026 → Dated Effect 4 source studies from September 2026; current versions live in pins.md. → [Read page](https://ratstack.sh/resources/effect-4-reference-projects)

- Full agent guide → An Effect stack so pure (aspirational) Kit Langton will blush. → [Read page](https://ratstack.sh/llms-full.txt)

- Handle expected failures without hiding defects → Declare expected failures, preserve their context, and recover by tag without turning crashes into successful outcomes. → [Read page](https://ratstack.sh/lore/error-model)

- HTTP success needs a status check and a decoder → Supply the client, check the response status, decode accepted data, and name failures at the adapter boundary. → [Read page](https://ratstack.sh/lore/http-responses-need-validation)

- Infrastructure as effects → Sam Goodwin relates application operations to infrastructure requirements in one typed program. → [Read page](https://ratstack.sh/lore/infrastructure-as-effects)

- Model-based testing → Describe what a system should do as a small model, generate action sequences, and check the real system against the model. → [Read page](https://ratstack.sh/lore/model-based-testing)

- One capability, every surface → A schema-typed contract and one handler feed every interface rat-stack keeps. → [Read page](https://ratstack.sh/lore/one-capability-every-surface)

- Rat Stack lore | rat-stack → Short, source-grounded notes on the ideas and decisions behind rat-stack. → [Read page](https://ratstack.sh/lore)

- Restart must reconstruct known state → Execution recovery and state recovery need separate contracts. → [Read page](https://ratstack.sh/lore/restart-from-known-state)

- Schema projections: 2026-09-18 history → Historical Effect rc.115 proposal and implementation receipts from 2026-09-18; use the one-capability-every-surface lore page for the current pattern. → [Read page](https://ratstack.sh/resources/schema-projections-and-code-mode)

- Scopes release resources when work ends → Acquire resources lazily, keep their use inside the owner, and release them after success, failure, or interruption. → [Read page](https://ratstack.sh/lore/scopes-own-resources)

- Source change history (log.md) → The generated change log as Markdown. → [Read page](https://ratstack.sh/log.md)

- Structure an Effect codebase by domain → Start with folders for application work, services and Layers in the same file, and startup code that supplies implementations. → [Read page](https://ratstack.sh/lore/structure-effect-by-domain)

- Supervision chooses what restarts → Restart policy follows dependency shape and bounds repeated recovery. → [Read page](https://ratstack.sh/lore/supervision-is-a-recovery-policy)

## Unlinked mentions

- [add-a-lifecycle-machine](https://ratstack.sh/skills/add-a-lifecycle-machine) → An actor is a running unit that processes events or performs work for a machine.

- [Agent rules for rat-stack (AGENTS.md)](https://ratstack.sh/AGENTS.md) → Only actions and actors declared in setupEffect contribute to the actor’s requirements; the xstate-effect/no-inline-effect lint rule enforces the inline cases.

- [Build and run rat-stack (README.md)](https://ratstack.sh/README.md) → 0: the official XState v6 to Effect 4 bridge: createEffectActor runs a machine as a scoped Effect, createEffectLogic makes Effects into actors with typed failures and requirements.

- [Code snippets quote a pinned source](https://ratstack.sh/lore/code-snippets) → There is no state machine yet.

- [David Khourshid](https://ratstack.sh/lore/david-khourshid) → It explains how to generate tests from a state machine instead of writing each case by hand.¶

- [Devtools](https://ratstack.sh/systems/devtools) → An actor is a running unit that processes events or performs work for a machine.

- [durability](https://ratstack.sh/skills/durability) → A running fiber, an in-memory queue, or a machine snapshot held only in memory is not that record.¶

- [Durability bibliography](https://ratstack.sh/lore/durability-bibliography) → Initial State for the State Machine¶

- [Effect + Alchemy peer patterns](https://ratstack.sh/resources/same-version-repos) → The declared actor uses fromEffect.

- [Effect + Alchemy peers](https://ratstack.sh/resources/peers) → Why this tier: Rat-stack-derived cost tracker pins Effect 4, XState 6 and the Effect actor bridge; worth tailing its CLI/MCP seam.

- [Effect 4 study: September 2026](https://ratstack.sh/resources/effect-4-reference-projects) → sleep on the Effect Clock so TestClock drives after, declared Effect actions fork in the actor scope, and R is collected from declared actions and actors via RequirementsFrom.

- [Handle expected failures without hiding defects](https://ratstack.sh/lore/error-model) → An unexpected actor failure or missing outcome is a programming failure at that boundary.¶

- [learn](https://ratstack.sh/skills/learn) → It stays on the machine.¶

- [Learn mode presents concepts at useful depths](https://ratstack.sh/systems/learn) → XState owns the learning lifecycle as a state machine: named states and the events that permit movement between them.

- [Learn rat-stack from your own agent](https://ratstack.sh/learn) → Progress stays on your machine in \~/.rat-learn/.

- [learn-rat-stack](https://ratstack.sh/skills/learn-rat-stack) → A declared createEffectLogic actor calls FileInspector.¶

- [Lint and type escape ledger (debt.md)](https://ratstack.sh/debt.md) → Reason: A machine-level error or an early stop is a programming error here: the domain failure travels through the unreadable state, not the actor.

- [Model-based testing](https://ratstack.sh/lore/model-based-testing) → XState builds it into xstate/graph: createTestModel wraps a machine, and getShortestPaths turns it into test paths.

- [Oxlint rule limits](https://ratstack.sh/resources/lint-rule-limits) → Covered now: Each assigned createEffectActor binding is matched to an identifier passed to watchActor; inline actor calls are matched separately.

- [Rat's Nest keeps the code public and the inventory private](https://ratstack.sh/lore/rats-nest) → Hostnames, IPs, sites, ports, and machine inventory live outside the repo.¶

- [security-audit](https://ratstack.sh/skills/security-audit) → critical — an unauthenticated actor gains code execution, full data-store access, or takeover of arbitrary accounts.¶

- [ship](https://ratstack.sh/skills/ship) → The release machine tracks deployment states and transitions.

- [test-behaviour](https://ratstack.sh/skills/test-behaviour) → The inspection lifecycle replays generated histories against the real actor:¶

- [Trace operations where they mean something](https://ratstack.sh/lore/trace-meaningful-operations) → Devtools records capability calls and watched actor lifecycles.

- [uncomplect](https://ratstack.sh/skills/uncomplect) → Actor: A running unit of work used by a machine.¶
