skip to content
Reference

The canonical lookup — how Loom is organized, what it writes to disk, and every tunable. Built for the reader who already knows what Loom does and needs the exact value.

No persuasion here. Jump to the one section you need from the contents rail, read the value at a glance — a table row, a code span, a glyph — and get back to your run. Every default, path, and tool name below is reproduced exactly; copy them, don't guess.

run identity

A run's id is <label>-<hash>: a readable label (a plan folds in its parent dir — docs/email-gateway/PLAN.mdemail-gateway-plan) plus a short stable hash of the plan's bytes. Identity is the plan's content, not its filename.

Why content-hashing. A generic PLAN.md recurs across projects; a name-derived id would collide, and a fresh worktree could inherit another project's all-DONE ledger and silently "resume" the wrong run. Hashing keeps distinct plans distinct, while a genuine re-run of the same plan resolves to the same id.

two guards back this

Resume keys on a live worktree.
Resume triggers on a live in-progress worktree, not the mere presence of a committed ledger — so re-running an already-merged plan reports "already built", not a phantom resume.
--clean starts truly clean.
--clean git rms + commits a previously-merged .loom/<plan>/, so a rebuild starts from a genuinely clean baseline rather than re-inheriting the committed ledger.

Per-project artifacts: .loom/<plan>/

Git-tracked, one subdirectory per plan. Runs are auditable, resumable, and travel with the worktree merge. Each plan's directory holds:

  • NN-slug.md — the sprint files (one per unit, with optional front-matter).
  • PROGRESS.md — the ledger; "done" is re-derived from it each run.
  • bin/gate — the synthesized pass/fail gate Loom runs itself.
  • logs/ — per-agent stage logs.
  • reports/ — any investigate-stage diagnoses.
  • about.md — the cheap-model project name + one-line description shown atop the monitor.
per-unit sprint front-matter

A sprint file may open with a leading --- block pinning per-unit overrides. Everything after the block is the brief the execute agent implements.

.loom/<plan>/04-migrate-store.md
---
effort: high
timeout_secs: 21600
no_progress_secs: 0
---
Migrate the on-disk store to the new layout. This unit is
unusually long and quiet, so it raises its own ceilings.
effort: — the agent reasoning effort for this unit.
timeout_secs: — override the absolute runaway backstop (LOOM_TIMEOUT) for this unit.
no_progress_secs: — override the stall window for this unit; 0 disables the stall guard, leaving only the absolute backstop.

Config — environment tunables

The complete tunable surface. Every default below is exact; means there is genuinely no fixed default (the value is unset).

read this right
LOOM_NO_PROGRESS_SECS is the stall window (reap on no progress), not a time limit; LOOM_TIMEOUT is the absolute runaway backstop. They are different ceilings — a long-but-busy unit is never reaped for merely taking a while.
variabledefaultmeaning
LOOM_MODELopusdefault model
LOOM_EFFORTmediumagent reasoning effort
LOOM_PLAN_EFFORThighplanning agents' effort
LOOM_COMMIT_MODELhaikucheap commit-message agent
LOOM_NO_PROGRESS_SECS1800 (30 min)per-unit stall window — reap an execute agent that emits no new output and no token movement for this long; 0 disables
LOOM_TIMEOUT14400 (4h)absolute runaway-backstop wall-clock ceiling — a true runaway bound, not a routine "split it" trigger
LOOM_PLAN_TIMEOUT3600ceiling for planning stages
LOOM_RETRY_DELAYS0,600,3600transient-retry backoff seconds (immediate, +10 min, +1 h, then give up — 4 attempts). Empty disables retry; malformed warns and falls back
LOOM_CONFIGconfig-directory override
LOOM_WINDOW_TITLEautoterminal window-title progress line (auto/1/0)
LOOM_SANDBOX
LOOM_SANDBOX_CMD
auto / —agent sandbox mode and custom wrapper (see Help)
LOOM_<ID>_BIN
LOOM_<ID>_MODEL
LOOM_<ID>_SMALL_MODEL
LOOM_<ID>_ARGS
per-harness config; <ID>CLAUDE/CODEX/OPENCODE/GEMINI (see Harnesses)
LOOM_CLAUDE
LOOM_MODEL
LOOM_COMMIT_MODEL
Claude back-compat aliases

Config — prompts

Global prompts live in ~/.config/loom — a symlink to the repo's config/ on the primary machine, or a real directory populated from the shipped defaults elsewhere. A per-project .loom/<plan>/<name>.md overrides any default prompt without forking the binary.

Shared prompts stay harness-neutral: Loom delivers the worker preamble through each harness's native system-prompt option, or prepends it where there's none.

prompt resolution — override wins over default
~/.config/loom              → symlink to the repo's config/ (primary machine)
config/execute.md           the shipped default execute-stage prompt
.loom/<plan>/execute.md      per-project override — used instead, no fork
runtime notes
The selected harness CLI owns billing and rate limits. Loom spawns it; the CLI's account and provider config apply.
The execute spend guard is no-progress, not elapsed-time. When it fires, Loom commits and gates the work; a salvaged green gate still advances the run.
Non-execute stages keep conservative terminal handling. They have no gate-green ground truth to salvage against.
Auto-compaction is disabled per unit. A unit that overflows fails loudly so the work can be split.
Headless events carry durable UTC timestamps. Persisted in .loom/<plan>/history.jsonl for replay and cold re-orientation.
attaching a brain over MCP

loom mcp <name> starts a stdio MCP server scoped to exactly one run, so an MCP-capable harness can inspect and steer it. It speaks JSON-RPC 2.0 over stdin/stdout and runs until the client closes stdin.

read surface — every path argument confined to the run

job_status list_runs read_ledger read_plan list_sprints read_report tail_history tail_log worktree_info

action surface

run_bash runs a command in the run's worktree, inside Loom's sandbox.
answer_prompt sends a real engine command into the run's live engine.
no live engine yet
The standalone server has no live engine to reach today, so answer_prompt fails cleanly with a clear tool-level error and touches no on-disk state — rather than half-applying a command against a dead run.

Register it as a stdio MCP server — command loom, args mcp <name>, cwd inside the repo:

.mcp.json
{
  "mcpServers": {
    "loom-run": { "command": "loom", "args": ["mcp", "my-run"], "cwd": "/path/to/repo" }
  }
}

Project layout

For the contributor orienting in the tree for the first time — what each top-level area holds before you open it.

pathwhat
src/domain/the RunEvent contract: Stage, SprintStatus, Outcome, …
src/engine/the orchestrator: config, agent, gate, ledger, worktree, plan, build
src/tui/the live monitor (Ratatui + mcurses-menu)
src/headless.rsthe plain-text front-end (same event stream as the TUI)
config/default agent prompts (overridable per project)