# Piece YAML Schema This is the reference for the piece YAML format consumed by `src/engine/piece-runner.ts` (`loadPiece` / `validatePieceDef`) and the `/api/pieces` HTTP layer (`src/bridge/pieces-api.ts` `validatePiece`). Field names are snake_case in the YAML; the engine maps them to camelCase internally (see `Movement` in `src/engine/agent-loop/types.ts`). > **Tool / edit / SSH availability is NOT declared in pieces.** > A piece defines only the *movement flow* (persona, instruction, transition > rules). Which tools an agent may call, whether `Write`/`Edit` are available, > and which SSH connections it can reach are all decided by the **workspace tool > policy** (Settings → Tools / SSH), resolved once per job by the worker > (`resolveWorkspaceTools`) and applied uniformly to every movement. The legacy > fields `allowed_tools`, `edit`, `shared_tools`, and `allowed_ssh_connections` > were removed in the tool-consolidation work — if present on a submitted piece > they are tolerated but ignored. ## Top-level | Field | Type | Required | Notes | |-------|------|----------|-------| | `name` | string | yes | lowercase `[a-z0-9-]+` | | `description` | string | yes | shown in the piece classifier | | `max_movements` | positive integer | yes | hard cap on movement count per run | | `initial_movement` | string | yes | must reference a `movements[].name` | | `triggers.keywords` | string[] | no | classifier hint only | | `required_mcp` | string[] | no | `[a-z0-9_-]{1,64}` server slugs | | `model` | string | no | preferred LLM model | | `movements` | Movement[] | yes | non-empty array | ## Movement | Field | Type | Required | Notes | |-------|------|----------|-------| | `name` | string | yes | unique within the piece | | `persona` | string | yes | system-prompt persona | | `instruction` | string | yes | the movement's task description | | `allowed_commands` | string[] | no | Bash command allowlist (overrides default) | | `rules` | Rule[] | yes | transition rules; may be empty | | `default_next` | string | no | engine-internal fallback (sentinel-friendly) | | `max_consecutive_revisits` | number | no | loop-detection threshold override | Tool availability, `Write`/`Edit` (edit) permission, and SSH connection scoping are runtime values on the in-memory `Movement` (populated from the workspace policy in `prepareMovementContext`), not YAML fields. ## Rule ```yaml - condition: next: ``` `rules[].next` may NOT use the reserved terminal sentinels `COMPLETE` / `ABORT` / `ASK` — those are reachable only through the `complete` tool (status: `success` / `aborted` / `needs_user_input`). `default_next` does accept the terminal sentinels because it is an engine-internal fallback (context overflow, ASK limit). ## Validation paths Two validators implement the same rules: - `validatePieceDef` in `src/engine/piece-runner.ts` — runs on every `loadPiece` (file-backed) and `CreatePiece` (runtime). - `validatePiece` in `src/bridge/pieces-api.ts` — runs on `PUT /api/pieces/:name` (UI editor). Both must stay in sync. When changing the schema, update both and add test coverage in `src/engine/piece-runner.test.ts` and `src/bridge/pieces-api.test.ts`.