Skip to main content
A Prompt is the judgment-bearing text you own. One shape, three forms:
  • a string literal — never touches disk;
  • { file: "./verify.md" } — re-read fresh at the start of every Round;
  • a per-round function (ctx) => string (sync or async).

Three homes

Every home takes a Prompt directly:
HomeRoleIf omitted
goalwhat “done” means — the set-once objective, judged every Roundrequired
executewhat to work onthe Goal stands in
verifythe success criteria the Verify agent judgesthe Goal stands in
import { Loop } from "@loop.js/core"

export default Loop.define({
  goal: "Build a playable 2D platformer in ./game",
  execute: { file: "./execute.md" },
  verify: "It builds, `bun test` passes, and the game boots to a controllable character",
})
Only goal is required — Goal-only is a first-class run mode. The framework ships no prompt generation: prompts are hand-authored or omitted. To bind a phase’s model or permissions, use the object form — the bare Prompt is shorthand for its prompt field:
verify: { prompt: { file: "./verify.md" }, model: "claude-haiku-4-5" }
{ file } next to a binding key ({ file: "./verify.md", model: "…" }) is ambiguous — is it a Prompt or a binding? — and is refused with a teaching error at startup, never guessed at. Write { prompt: { file }, model }.
Paths named in a prompt resolve inside the Workspace: with the default layout, "./game" means workspace/game.

File prompts are re-read every Round

{ file } is read fresh at the start of each Round. Edit the file while the Loop runs and the next Round works — and is judged — against the new text: editing verify.md mid-loop re-sets the bar. A missing or unreadable file is a loud error naming the path, never a silent fallback.
The Execute agent may itself author or revise verify.md — every edit is visible in the event stream.

Function prompts

The function form receives PromptCtx — a deliberately small per-round context, the same for all three homes; enough to vary the prompt, not a state channel:
goal: (ctx) =>
  ctx.previous
    ? `Address the last verdict first: ${ctx.previous.feedback}`
    : "Build a playable 2D platformer in ./game",
ctx.round is the 1-based Round number. ctx.previous.feedback carries what the last Round left behind — the Verdict’s reason, or the engine’s one-liner if the Round was interrupted; ctx.previous.verdict is present when a Verdict was produced.

Writing a good Verify bar

A good bar names a testable end-state, the checks to run, and the invariants that must hold. A vaguer bar is judged conservatively: the skeptical Verify agent treats a claim as evidence, not proof, and insufficient evidence reads as not met.