---
name: actors
description: Required practical guide for non-trivial pi-actors use, including parallel actor launches, subagent fanout, and autonomous coordinator workflows. Read before using or changing spawn, message, inspect, actor runs, tools, recipes, command templates, async lifecycle, mailboxes, artifacts, and local orchestration mechanics.
metadata:
  version: 0.42.0
---

# Actors (pi-actors)

`pi-actors` turns trusted local capabilities into addressable actors. This skill is the required practical layer for any non-trivial pi-actors use or repo change: tools, nouns, lifecycle, message protocol, recipes, and common edge cases. It is not a multi-agent strategy guide; use a swarm skill for decomposition, quorum design, reviewer lenses, and consensus methodology.

Maintain this skill as the extension's agent-facing manual. When implementation changes reveal new durable mechanics, invariants, warnings, or safer operating patterns, update this skill alongside code/docs so future agents learn the current actor model instead of rediscovering it.

## Knowledge Surfaces

Context arrives in layers:

- **Injected prompt**: always present at extension load. It is a bootstrap/reminder of current verbs, paths, and runtime rules; it should not try to be documentation.
- **Skill header**: automatically matched by agents from `name`/`description`. Its job is to signal: if pi-actors use is unclear, read this skill body.
- **This skill body**: highest-density practical reference. It should explain extension operation from multiple angles and link to deeper docs without becoming a changelog or swarm-methodology guide.
- **README**: human entrypoint. It explains what pi-actors is, why it matters, benefits, rhythm, and representative scenarios; it is not automatically in agent context.
- **Docs**: transportable standards by domain: command templates, recipes, async runs, actor messages, registry, recipe library; read on demand.
- **AGENTS.md**: project context for agents changing pi-actors: architecture constraints, durable conventions, do/don't rules, validation; read for repo work.

## Core Nouns

```text
Trusted local capability
  -> Command template          execution graph
  -> Recipe                    saved actor definition
  -> spawn                     starts one run instance
  -> run:<id>                  addressable actor

Trusted local capability
  -> Command template or recipe
  -> register_tool             persists an agent-callable wrapper
  -> tool:<name>               addressable tool actor
```

- **Command template**: portable execution graph. String leaf, sequence array, or object node with controls.
- **Recipe**: saved JSON definition wrapping a template with args, defaults, imports, mailbox, artifacts, metadata, and optional `async: true`.
- **Run actor**: one detached execution instance addressable as `run:<id>` with status, logs, messages, mailbox metadata, files, and artifacts.
- **Tool actor**: registered persistent capability addressable as `tool:<name>` and callable through the generated tool or `message`. `tool:pi-actors` is reserved for runtime status and explicit automatic-review retry/reset control.
- **Artifact**: named durable output path declared by a recipe/run.
- **Mailbox**: interaction contract: message types the actor accepts/emits.
- **Advanced group/coordination surfaces**: `branch:<run>/<branch>`, `room:<run>`, `coordinator`, `session:<id>`, and communication snapshots exist for multi-actor workflows and diagnostics; do not make them the default mental model.

## Three Verbs

Actor-mode trigger: if work may outlive this turn, needs steering/follow-up/artifacts, runs as a service, fans out, or should be resumed/inspected later, use `spawn → message → inspect` instead of ad hoc shell backgrounding. Keep short foreground checks as normal tools.

### `spawn` — create a run actor

Use for long work, background services, subagents, fanout, pipelines, and reusable recipes.

Before a parallel launch, the coordinator should verify five things once: each actor owns a disjoint mutation scope, every run has a stable id, each durable result has an artifact path, every command template respects shell-free argv execution, and completion can return through follow-up delivery without polling. For multiple delegated implementation or review actors, also load the bundled Swarm skill before choosing decomposition, locks, or quorum shape.

```json
{
  "as": "run:repo-health",
  "file": "pipeline-repo-health",
  "values": { "path": "/repo" },
  "artifacts": { "report": "/tmp/repo-health.md" }
}
```

Rules:

- Command-template strings execute directly without a shell. Operators such as `&&`, `||`, pipes, redirects, and `cd` remain literal argv unless an explicit trusted shell is the executable. Prefer absolute paths or template arrays for sequencing; put non-trivial shell behavior in a reviewed script.
- Use `file`/`recipe` for saved recipes; bare names resolve under `~/.pi/agent/recipes`.
- Use inline `template` for one-off experiments; promote useful repeats to recipes.
- When a successful actor follow-up suggests persistence, decide whether the pattern deserves durable tool memory; call `register_tool` yourself only when the evidence is strong, and ask before writing the user recipe root.
- Use stable `as` names when you will inspect or message the actor later.
- Public run state is runtime-owned; do not pass custom `state_dir` paths. This keeps `run:<id>` addressability and retention on one boundary.
- `async: true` on the recipe is the detached run switch.

### `message` — send a typed envelope

```json
{
  "to": "run:repo-health",
  "type": "control.cancel",
  "summary": "Cancel stale repo-health run",
  "body": {}
}
```

Envelope fields:

- Required: `to`, `type`.
- Useful: `summary`, `body`, `from`, `reply_to`, `correlation_id`, `metadata`.
- Core addresses: `run:<id>`, `tool:<name>`.
- Advanced addresses: `branch:<run>/<branch>`, `room:<run>` for group timeline/roster, `coordinator`, `session:<id>`.
- Group posts to `room:<run>` require `from` from the same run (`run:<run>` or `branch:<run>/<branch>`).
- Runtime termination message: `control.kill` is the only documented actor message that kills a run. `control.stop` and `control.cancel` are actor-local mailbox vocabulary only when a recipe declares and handles them. Terminal retention messages: `control.archive`, `control.prune`.
- Long-lived child processes should remain in the run-owned process group unless the recipe implements an explicit daemon termination bridge; do not leave unowned detached services behind `control.kill`.
- Run controls revalidate a persisted cross-platform process identity proof at authorization and again immediately before signaling. Treat `dead pid`, `owner mismatch`, and `unsupported proof` as distinct fail-closed states; on Unix, only process-group `ESRCH` plus one more matching identity check permits exact-pid fallback, while permission/authorization errors remain terminal. Node exposes no portable pidfd/process-group handle, so retain the documented residual exit/reuse window instead of claiming atomic signaling or bypassing control with direct pid signals.
- Detached actors survive ordinary agent turns. On `session_shutdown` (quit, reload, or session replacement), pi-actors attempts canonical `control.kill` for each discovered readable still-running exact-owner run. Control compares immutable run generation inside the canonical boundary and serializes against same-directory restart; terminal, ambiguous, changed-generation, and other-session runs remain untouched. Teardown scans without the ordinary index depth cap, reports unreadable/corrupt state as failures, and persists a bounded summary under the run root. Descendant Pi sessions remain separate owners and rely on their own shutdown hooks; hard host termination can still leave an orphan requiring summary-guided OS/manual recovery, so never describe teardown as an absolute no-survivor guarantee.

Check `inspect view=mailbox` before domain-specific messages.

### `inspect` — observe intentionally

```json
{ "target": "run:repo-health", "view": "status" }
{ "target": "run:repo-health", "view": "tail", "lines": "80" }
{ "target": "run:repo-health", "view": "messages" }
{ "target": "run:repo-health", "view": "artifacts" }
{ "target": "tool:pi-actors", "view": "status" }
{ "target": "tool:music_player", "view": "status" }
{ "target": "recipes", "view": "status" }
{ "target": "coordinator", "view": "status" }
```

Views:

- `status`: lifecycle, pid, values, progress, result, compact summary.
- `tail`: recent stdout/stderr/log tail.
- `messages`: actor messages emitted by the run, or room timeline entries for `room:*`.
- Advanced `communication`: run/branch group-coordination snapshot with self/root/default-room/member/contact hints.
- Advanced `roster`: room member list with address, role, parent, caps, claim, status, and last seen.
- Advanced `contacts`: roster-derived direct-message targets without full roster metadata.
- Advanced `previews`: TUI-ready bounded group-message previews with timestamp/from/to/type/summary/body_preview.
- `mailbox`: declared accepts/emits contract for runs; queued direct branch inbox messages for `branch:<run>/<branch>` with `id`, status, route/type, and queue/handling timestamps.
- `files`: run state file summary plus a `lines`-bounded `review-evidence.json` manifest when present, including total/truncated command counts and stage capture paths.
- `artifacts`: declared artifact paths/status plus the same bounded owned review-evidence manifest when present.
- `recipes` target: registry summary for active, shadowed, invalid, disabled, and diagnostic recipe entries.

Let terminal notifications arrive. They queue through Pi's follow-up delivery mode, so a busy coordinator finishes its current work before receiving concurrently completed actor results; the host's `followUpMode` controls whether queued results arrive together or one at a time. File watching accelerates delivery, while a bounded ten-second terminal-only reconciliation pass recovers missed or failed watcher activity without replaying outbox traffic; watcher degradation and rearm remain visible diagnostics. When a deferred actor result gates the next step, wait for that terminal follow-up instead of scheduling continuation loops, repeatedly inspecting, or mutating the actor's reviewed scope. Idle coordinators still start a normal turn through `triggerTurn: true`. Inspect early only for an operator request, a meaningful actor event, or diagnosis of an overdue or stuck run.

## Runtime Communication Rules

- Keep one public communication model: `spawn` creates actors, `message` sends typed envelopes, and `inspect` observes. Avoid adding public side channels or storage nouns when a normal actor address/view can express the operation.
- Keep route and semantic type separate. Direct, room, coordinator, and session messages may share `type`; delivery behavior comes from `to`.
- Treat persisted communication logs as recipe evidence. Use `inspect room:<run> view=messages|previews` and `inspect run:<id> view=communication` to improve mailbox/artifact conventions after real runs.
- Any UI, summary, or aggregate view that scans run directories must apply coordinator/session ownership filters before exposing summaries or body previews.
- Treat `communication.json` as visible actor context, not a global mutable truth table. Run-level snapshots should identify the run actor; branch-local snapshots should identify the branch actor.
- Prefer same-run provenance checks on lateral actor routes. If `from` is accepted for room or branch routes, validate that it belongs to the addressed run.

## Command Template Standard

Forms:

```json
"npm test -- {file}"
["npm run typecheck", "npm test"]
{ "parallel": true, "template": ["job-a", "job-b"] }
```

Controls:

- `args`, `defaults`: public placeholder declarations and defaults.
- `parallel: true`: fanout child nodes.
- `when`: conditional execution.
- `accept_output: review_evidence`: fail closed unless the exact first non-whitespace stdout line is `ACTOR_REVIEW_RESULT`; marker prefixes are rejected and rejected stdout remains diagnostic evidence.
- `timeout`, `delay`, `retry`: timing and retry controls; string placeholders are allowed where supported.
- `failure`: `continue`, `branch`, or `root` propagation.
- `recover`: cleanup between retry attempts.
- `repeat`: repeated node expansion.
- `output`: output behavior selection.
- Command stdout/stderr use bounded tails plus complete spill files; tool/run diagnostics expose byte counts, truncation, and spill paths, while pipelines fail with `incomplete pipeline stdin` rather than consuming a partial tail.
- Detached child `pi -p` commands receive isolated session storage under `sessions/command-NNN` in their owned run state, and command evidence records any resulting JSONL files. Coordinator-managed room/swarm participants use role/phase-scoped directories under the same run-local `sessions/` root so their turns remain discoverable too. Explicit `--no-session`, `--session`, `--session-id`, `--session-dir`, or `--fork` policy remains caller-owned and is never replaced.
- Persisted child-session inspection follows the latest JSONL entry branch, correlates tool results by call id, bounds previews, and redacts common secret-bearing fields/text. Thinking content is evidence only when Pi persisted an explicit `thinking` block; never infer or advertise hidden reasoning.

Placeholders:

- `{name}` required value.
- `{name=default}` inline default.
- `{name:type=default}` typed inline arg.
- `{value??fallback}` nullish fallback.
- `{flag?yes:no}` ternary fallback.

Templates are synchronous and portable. Recipes give them identity and lifecycle.

## Recipe Standard

Minimal actor recipe:

```json
{
  "async": true,
  "args": ["path:path", "model:string"],
  "defaults": {},
  "mailbox": {
    "accepts": ["control.kill"],
    "emits": ["command.done", "run.done", "run.failed"]
  },
  "artifacts": { "report": "{path}/report.md" },
  "template": "some-command {path} --model {model}"
}
```

Rules:

1. Every recipe owns `template` directly.
2. `async: true` makes spawned work a detached actor run.
3. Public knobs belong in `args`/`defaults`; hidden launch mechanics stay inside `template`.
4. Use `imports` to compose recipes; imported recipes are definitions, not nested async runs.
5. Direct recipe delegation is the thin-wrapper case: when a `template` value is just a ready recipe name/path, the intended behavior is to delegate to that recipe rather than execute the recipe file as a program. Use this for simple handoffs and wrapper tools; use `imports` + `{ "name": "alias" }` when you need rich composition, multiple nodes, or import-specific values/defaults.
6. When exposing an already-authored recipe as a user tool before direct delegation is available or when composition is needed, make a small wrapper recipe in `~/.pi/agent/recipes` that imports the source recipe and uses a `{ "name": "alias" }` node. Do not copy the ready recipe's script command, defaults, mailbox, or artifacts into a second template.
7. Declare `mailbox` for actors that accept or emit meaningful messages.
8. Declare `artifacts` for durable outputs the coordinator should inspect.
9. File-backed recipe identity comes from the filename basename; legacy top-level `name` fields are ignored by loaders.
10. File-backed async recipes pass child `pi -p` actors a bounded JSONL recipe context bundle by default: raw entry/import recipe records, derived `name`, import path/alias, and `"you_are_here": true` on the launching recipe node. The runner collapses all natural-language positional fragments into one prompt under `prompts/command-NNN.md`, keeps intentional `@file` attachments separate, and invokes Pi with one authoritative prompt-file arg so large prompts and recipe context stay inspectable and argv-safe. Set `"actor_context": false` or `"off"` to suppress recipe context for minimal prompts.
11. Keep packaged recipes generic: no machine-local paths, no private companion identities, no project-specific defaults unless the recipe is explicitly project-specific.
12. Do not ship concrete model-version defaults in packaged recipes. For review-oriented subagent/lens recipes, default model/thinking args through `{current_model}` and `{current_thinking}` so they inherit the selected Pi session policy; keep `model`, `models`, `thinking`, and stage-specific model args explicit so callers can override policy at launch.

Priority for same-id recipes:

1. No recipe: no capability.
2. Packaged pi-actors recipe: standard-library declarative actor component.
3. Explicit ad hoc user recipe file outside `~/.pi/agent/recipes`.
4. User recipe in `~/.pi/agent/recipes/*.json` or `*.md`: highest-priority operator tool surface.

Only matching filename ids compete. Higher priority shadows lower priority; within one priority layer, same-id JSON shadows Markdown. Same-id overrides are normal composition/delegation behavior, not startup-warning material. An invalid or `disabled: true` higher-priority recipe blocks fallback so the agent does not silently run standard-library behavior when a user override is broken or intentionally disabled.

Muscle-memory lens: pi-actors has two durable executable-memory layers.

1. `~/.pi/agent/recipes/*.json` and `*.md` are the agent's active capability memory. Every recipe in that directory becomes an easy-to-call tool automatically and survives into later sessions. Descriptions matter here because they become the tool's operator-facing title/context.
2. `~/.pi/agent/recipes/drafts/*.json` is draft memory captured from successful inline `spawn template=...` runs. Drafts do not enter the injected tool surface and remain reusable by explicit path, e.g. `spawn file="~/.pi/agent/recipes/drafts/<name>.json"`.

Agents grow draft memory by trying ad hoc actors successfully. Active memory grows through explicit `register_tool`, deliberate operator recipe edits, or the bounded automatic review cycle. Treat drafts as the workbench and root recipes as active muscle memory.

Usage lens: user recipe launches update an extension-maintained canonical name-and-priority lineage ledger under `.usage/recipes/<recipe-name>.json`; authored recipe files are not rewritten for telemetry. Accounting briefly shares the portfolio mutation fence so source quarantine cannot erase an authorized launch; if another session already changed the source, the stale invocation rejects and requests reload rather than executing without evidence. Lifetime calls survive rename, revision, promotion, and demotion, while revision-local calls restart when executable content changes. The bounded unversioned ledger retains former names/paths, revision ancestry, transition events, and review epochs. Discovery merges lineage usage into inspection. Agents should not hand-edit counters; usage remains evidence rather than a sufficient usefulness verdict.

Automatic review lens: successful transient/ad hoc actor runs leave replayable drafts rather than active tools. At twelve eligible drafts, pi-actors captures one exact trusted batch and attaches only its identity-opaque value-free structural projection to a silent no-tools reviewer after the foreground turn and active actors finish. Its complete quota-free `promote`/`discard` result contains no recipe content: the deterministic executor derives promotions from exact captured sources and revalidates source/target CAS, complete recipes, root identity, quarantine hashes, and recovery state before commit. Newer drafts remain for a later batch; malformed, stale, unsafe, or incomplete decisions fail closed. Unchanged automatic demotions remain in cooldown until their executable fingerprint changes. Prefer fenced `register_tool draft=...` for an explicit single-draft promotion. A deliberate move/copy into the recipe root also remains valid, but may invalidate and defer an already captured batch; do not reconstruct removed batch commands or ask the operator to drive an automatic batch.

Portfolio lens: thirty-six eligible non-sensitive active revisions trigger a no-tools review of an attached value-free structural projection; canonical names, draft basenames, raw hashes, recipe bodies, template/default values, authored prose, and filesystem paths remain in the separate trusted capture; batch-local occurrence IDs and equality-only content groups preserve correlation and deduplication. Set `PI_ACTORS_AUTOMATIC_REVIEW=off` before Pi starts to disable both reviewer scheduling and safe-boundary portfolio activation; verify the effective value with `inspect target=tool:pi-actors view=status`. The reviewer may select keep, unchanged-source rename, unchanged-source demotion, or deduplication of canonically identical captured recipes; it cannot return recipe content. Replacement, split, and executable contract changes require explicit operator authoring. Approval remains immutable until the next safe session boundary, where journaled filesystem and lineage executors apply only the captured recipe bytes. Use `inspect target=recipes view=reviews` for bounded evidence including failed stage/error/next action. Recover a failed cycle through `message to=tool:pi-actors type=review.retry body={"scope":"draft"|"tool"}`. Draft retry resumes an existing authenticated transaction plan and original reviewer run rather than generating decisions after filesystem commit; `review.reset` clears only disposable terminal admission state and rejects tool recovery evidence that must roll forward.

## Registered Tools

`register_tool` persists trusted local capabilities as recipe files in `~/.pi/agent/recipes/*.json`; hand-authored Markdown recipes in the same directory are also discovered as tools.

Use it when a command/template/recipe should become durable agent muscle memory. Prefer typed args or placeholder-derived args; use `update=true` for replacement and `template=null` or `template=""` for deletion. `register_tool` should create/update/delete simple recipe files in the user recipe root; direct recipe-file editing is the right path when the wrapper needs `imports` or other top-level recipe metadata not exposed by the interactive mutation API.

Ready-recipe registration patterns:

Thin delegation target shape:

```json
{
  "description": "Run a ready recipe through a local tool name.",
  "args": ["source:path", "volume:int=70"],
  "template": "/path/to/ready-recipe.json"
}
```

Delegation is for one-to-one handoff: expose or call a maintained recipe directly, preserving that recipe as the source of truth. If the runtime does not yet support direct recipe references in `template`, or if you need composition, use the import-node wrapper below.

Composition/import wrapper:

```json
{
  "description": "Run the ABCd context validator through its skill recipe.",
  "imports": {
    "validate_context": "{agent}/skills/abcd-context/recipes/validate-context.json"
  },
  "args": ["path:path=."],
  "template": { "name": "validate_context" }
}
```

Use delegation or this import pattern whenever a reusable recipe already exists: packaged pi-actors components, project-local recipes, ad hoc reviewed recipe files, and especially skill-owned recipes that wrap skill scripts. The wrapper owns only the public tool name, description, optional narrowed args/defaults, and local usage metadata. The delegated/imported recipe remains the source of truth for the script path, default values, mailbox contract, artifacts, and future fixes.

Tool-registration lenses are open-ended prompts for deciding what deserves durable tool status:

1. **Reliability lens**: register wrappers for operations where agents commonly omit checks, run steps out of order, pass ambiguous inputs, or recover poorly from partial failure.
2. **Safety lens**: prefer read-only diagnostics, dry-runs, preflights, confirmations, or bounded adapters around high-impact operations before registering direct action tools.
3. **Context-affordance lens**: register tools whose mere presence in the injected capability list should steer agents toward the right operational habit.
4. **Existing-recipe lens**: scan already-authored recipes before inventing a new tool. Packaged recipes, ad hoc project recipes, and recipes co-located under skill directories are the first candidates to delegate to or import from a user-root wrapper when they match a recurring local workflow.
5. **Skill-recipe lens**: when a skill ships a recipe for its script, local tools must delegate to or import that recipe instead of calling the skill script directly. This preserves the skill's maintained interface and keeps future script/default changes centralized.
6. **Composition lens**: register small semantic entrypoints over reusable recipe components instead of baking one large scenario-specific shell command into a tool; prefer direct delegation for one recipe, imports for composed graphs.
7. **Portability lens**: keep recipe files transportable; make tool exposure a consequence of placement in `~/.pi/agent/recipes`, not recipe-owned markers or machine-local assumptions.

Default bias: register diagnostic/preflight tools before action tools, and promote existing recipes before writing new orchestration. A good persistent tool shrinks the chance of a subtle operational mistake, not just the number of keystrokes.

Tool templates may be:

- A foreground command template.
- A file-backed recipe name/path for thin delegation.
- A complete recipe body, optionally `async: true`.

The user recipe root is the default tool set by location. It accepts canonical JSON recipes and literate Markdown recipes with frontmatter plus fenced `template`/`json recipe` blocks; same-id JSON shadows Markdown in the same priority layer. Packaged recipes are lower-priority standard-library components and are not tools unless copied or registered into the agent recipe root. Ideal runtime behavior is reactive: create/edit/delete recipe files, validate them, then connect valid tools or surface diagnostics without requiring agents to hand-maintain a separate registry.

## Top Recipes

Use packaged recipes by name with `spawn file=<name>` for async actors, or register/call them as tools when repeated use deserves a stable shortcut.

Packaged review recipes are directly spawnable. Use `spawn file="pipeline-review-readiness" values={...}` for readiness review or `spawn file="subagent-review" values={...}` for one reviewer; pass model/thinking/tool policy through values, then inspect the run. Review coordinators preflight stage models before fanout; `ACTOR_PREFLIGHT_FAILED` diagnostics identify the failed stage, selected policy, provider error class, prompt file, and override args. Review stages require the `ACTOR_REVIEW_RESULT` evidence marker, so format acknowledgements and input requests fail closed before satisfying quorum or flowing downstream; rejected stdout remains in branch diagnostics. Quorum-aware review fanout exposes `subagent_ttl_ms`, `reviewer_concurrency`, `min_successful_reviewers`, and `merge_policy`; partial reviewer evidence is preserved and marked `complete`, `degraded`, or `insufficient_data`. Run status/progress exposes `model_policy` so inherited vs explicit model/thinking choices remain visible. Do not recreate their script commands, call packaged scripts directly, or create wrapper recipes just to launch the maintained recipe.

- [`pipeline-room-swarm`](../../recipes/pipeline-room-swarm.json): room-visible swarm coordination with roles, rounds, optional locker, artifact synthesis, and `subagent_ttl_ms` for hard participant budgets.
- [`pipeline-repo-health`](../../recipes/pipeline-repo-health.json): git/doc/validation evidence → normalized repository health report.
- [`pipeline-release-readiness`](../../recipes/pipeline-release-readiness.json): changelog/package/skill/validation evidence → release review → artifact report.
- [`actor-worker`](../../recipes/actor-worker.json): canonical mailbox-backed branch worker reference for claim/handle/status/artifact patterns.
- [`coordinator-locker`](../../recipes/coordinator-locker.json): queue + lease locks + journaled coordinator messages for multi-actor ownership.

## Deep References

- `docs/actors-deep-reference.md` — recipe navigator, operating patterns, lifecycle discipline, pitfalls.
- `docs/command-templates.md` — execution graph semantics.
- `docs/template-recipes.md` — recipe storage, imports, defaults, references.
- `docs/async-runs.md` — detached lifecycle, state, cancellation, observability.
- `docs/actor-messages.md` — addressed envelope protocol and mailbox model.
- `docs/tool-registry.md` — persistent tool registry and generated tools.
- `docs/recipe-library.md` — packaged recipes.
- `docs/task-first-recipes.md` — deriving reusable pipelines from operator tasks.
- `docs/component-recipes.md` — reusable coordinator/subagent building blocks.

## One-Sentence Contract

Use pi-actors to wrap local capabilities as addressable actors: define launch with templates, preserve semantics in recipes/tools, start with `spawn`, communicate with `message`, observe with `inspect`, and hand off durable results through messages and artifacts.
