---
title: Mechanism events
description: The events that jr2 itself sends into every jr2Setup Machine, and their payloads.
---

# Mechanism events

jr2's own mechanism delivers these events into every Machine that `jr2Setup` makes, beside the workflow events its `defineEvent` defs declare. Handle one with an `on` key, as any event: `on: { "agent.fault": "failed" }`. A Mechanism event's name has a dot, and a workflow event's name cannot, so the two never collide. The Machine's event type, `WorkflowEvent`, holds them already.

Each Mechanism event must have a route. `jr2 check` and `jr2 up` refuse a state that invokes an Agent, or an Open body that lends an Agent or allows a model, when that state and each state above it have no `agent.fault` route, and `workspace()` refuses a body with a state that is not final and has no `workspace.lost` route on itself or on a state above. One route on the root satisfies each rule. A route with actions only is a route. When the event comes and no transition takes it, because the guard of its route is false, the run ends `error`, and its fault names the event, the state and the reason. In a [Chart](../concepts/charts.md), the `agent.fault` route is optional: when no transition of the Chart takes it, the Open body sends it, unchanged, to the Machine that invoked the body, and that Machine's own route takes it.

To end the run as a Failure when a Mechanism event comes, route it to a state that invokes [`fail`](authoring-surface/fail.md): `lost: { invoke: { src: "fail", input: { reason: "the Sandbox is gone" } } }`. The run then ends `failed`, not `done`, because a final state means that the work succeeded.

<a id="type-mechanismevent"></a>`MechanismEvent` is one of these events: [`agent.fault`](#agent-fault), [`workspace.lost`](#workspace-lost).

<a id="agent-fault"></a>

## `agent.fault`

- Type: `FaultTelemetry`
- Sent by: The Agent slot (`agent()`) that the state invoked, to the Machine that invoked it.

```ts
type FaultTelemetry = {
  type: "agent.fault";
  instanceId: string;
  reason: string;
};
```

jr2 sends `agent.fault` to the Machine when an Agent's Turn cannot go on: the Harness failed
the Turn, or the Agent ended its Turns with no pick after every nudge. The Machine decides what
to do next. Route it in the state that invokes the Agent, or in a state above it, with
`on: { "agent.fault": … }`; one route on the root is a route for each state. `jr2 check` and
`jr2 up` refuse a state that invokes an Agent, or an Open body that can run one, with no route for
it. When the event comes and no transition takes it (the route's guard is false), the run ends
`error`, with the event, the state and the reason in its fault. In a Chart the route is optional:
when no transition of the Chart takes the event, the Open body sends it, unchanged, to the Machine
that invoked the body, whose route takes it.

**Payload**

- `instanceId`, `string`: The Instance ID of the Agent's conversation that failed: the same id on each Turn that continues it.
- `reason`, `string`: Why the Turn failed, as text for a person or a log: the Harness's error, or that the Agent ended its Turns without a pick after every nudge. Route on the event, not on this text.

<a id="workspace-lost"></a>

## `workspace.lost`

- Type: `WorkspaceLost`
- Sent by: `workspace()`, to its body Machine, when the Workspace's Sandbox is gone.

```ts
type WorkspaceLost = {
  type: "workspace.lost";
  reason?: string;
  message?: string;
};
```

jr2 sends `workspace.lost` to the body of a `workspace()` when its Sandbox is gone: its pod was
evicted or deleted, or its node went. jr2 does not make the Sandbox again, and the commits that
were not pushed go with it. The body decides what to do next: route the event with
`on: { "workspace.lost": … }`, usually once, on the body's root. `workspace()` refuses a body
with a state that is not final and has no route for it on itself or on a state above. When the
event comes and no transition takes it (the route's guard is false), the run ends `error`, with
the event, the state and the reason in its fault.

**Payload**

- `reason` (optional), `string`: Why the Sandbox is gone, for a person or a log: the pod's reason (`Evicted`, `NodeShutdown`), the operator's (`PodDeleted`, `NodeLost`), or `Deleted`. A body can act without it.
- `message` (optional), `string`: More text about the loss, when jr2 has it.
