Lever contains the Ralph loop and task agent for running Codex-driven tasks against a repo. The Rust lever binary is the canonical entry point.
- bash
- jq
- git
- python (for token estimates and rate limit bookkeeping)
- codex (Codex CLI)
Build the lever CLI with Cargo:
cargo install --path .This installs lever into your Cargo bin directory (usually ~/.cargo/bin). Add that directory to your PATH if it is not already present. For local development you can also run cargo build --bin lever and reference target/debug/lever directly.
The default prompt is prompts/autonomous-senior-engineer.prompt.md under the workspace. If you run lever in a repo without that file and do not supply --prompt, lever will fail with Prompt file not found: <workspace>/prompts/autonomous-senior-engineer.prompt.md. Pass --prompt /path/to/prompt.md in that case.
The lever binary is the canonical entry point. Run lever once to execute the next runnable task (status != completed, model != human), add --task-id <id> to pin a specific task, use --next to force "next runnable" selection, or pass --loop (see below) to keep invoking task-agent runs until a stop reason occurs.
--tasksdefaults toprd.json, falling back totasks.jsonin the current directory if the flagged file is absent.--workspacedefaults to the current directory; when--tasksis omitted the tasks file is discovered relative to that workspace.--promptdefaults toprompts/autonomous-senior-engineer.prompt.mdunder the workspace; the CLI validates the file exists before running.--command-pathdefaults tointernal(the Rust task agent). You can point it at another executable for testing or for delegating work to a different task agent binary.- Every iteration forwards the resolved
--tasks,--workspace, and--promptvalues to the configured task agent so the behavior stays consistent with the legacy workflow. --assigneeis forwarded to external task agents when--command-pathis notinternal.--reset-taskclears attempt counters for the selected task before running.--delayinserts a sleep between loop iterations (seconds, default 0; only valid with--loop).--nextselects the first task whose status is notcompletedand whose model is nothuman; it cannot be combined with--task-id.--context-compileenables context compilation for each run and--no-context-compiledisables it;--context-failure-policy <best-effort|required>selects how failures are handled (default: best-effort);--context-token-budget <TOKENS>sets the context compilation token budget (default: 8000);--assembly-path <PATH>overrides the Assembly executable used for context compilation (default:assembly);--prompt-lint-summaryinjects a concise lint summary frompack/lint.jsonwhen available.
Context compilation runs Assembly before prompt construction to build a context pack under .ralph/runs/<task_id>/<run_id>/pack. The task agent then injects a lint summary and compiled context into the prompt when available.
Flags:
--context-compileenables context compilation for each run.--no-context-compiledisables context compilation (no Assembly run, no context-compile report).--context-failure-policy <best-effort|required>controls failure handling; defaultbest-effort.--context-token-budget <TOKENS>sets the token budget (must be >= 1; default 8000).--assembly-path <PATH>overrides the Assembly executable (defaultassembly).--prompt-lint-summaryinjects a summary frompack/lint.jsonwhen available.
Lifecycle and artifacts:
- When enabled, Lever validates the Assembly CLI contract and runs
assembly buildto generate the pack. - Required pack outputs are
manifest.json,index.json,context.md,policy.md, andlint.json. - Assembly artifacts are written under each run:
assembly-task.json,assembly-summary.json,assembly.stdout.log, andassembly.stderr.log. - The context compile report lives at
.ralph/runs/<task_id>/<run_id>/context-compile.jsonand includesenabled,status(skipped|succeeded|failed),policy(best-effort|required),policy_outcome(skipped|proceeded|continued|blocked),pack_dir,pack_files, andpack_missing. - When present, the lint summary is inserted before compiled context; compiled context appends a provenance line referencing
pack/manifest.jsonand the current git commit.
Failure behavior:
- best-effort continues without compiled context and lint summary; the report records
status=failedandpolicy_outcome=continued. - required blocks the run, marks the task
blocked, and exits with code13after writing the report (policy_outcome=blocked).
--loop accepts an optional count. Passing --loop with no value (or --loop 0) keeps cycling until a terminal stop reason occurs (no tasks, human input request, blocked run, etc.). Any positive integer limits the number of task-agent invocations; once the limit is reached, lever logs lever: --loop limit reached (<count>) and exits even if runnable tasks remain. Without --loop, lever runs only one iteration, so you can rely on the existing --task-id or implicit selection behavior for ad-hoc task-agent runs.
lever mostly forwards the task agent’s exit code. In loop mode it interprets some codes to decide when to stop.
0: Success (single iteration completed) or loop ended normally (no remaining tasks, loop limit reached, or clean shutdown).1: Lever stop reason (human input required, blocked by dependencies, or blocked run detected in loop mode).2: Invalid task metadata, unsupported model, or invalid task selection input.3: Task agent reports no runnable tasks.4: Task agent selected a human task.6: Task agent reports a dependency ordering issue.10: Task agent blocked because noresult.jsonwas produced.11: Task agent blocked (attempt limit reached before run).12: Task agent recorded progress (run completed without deterministic success).13: Task agent blocked because Assembly context compilation failed with--context-failure-policy required.130: Interrupted (SIGINT/CTRL-C).
# Continuous loop (same as `--loop 0`)
lever --loop --tasks prd.json# Fixed iteration loop
lever --loop 3 --tasks prd.jsonPrefer lever for new automation and tooling.
If you want a single stream you can tail with lnav, pipe stdout+stderr:
lever --loop --tasks prd.json 2>&1 | lnav -To keep a copy while watching:
lever --loop --tasks prd.json 2>&1 | tee .ralph/ralph.log | lnav -When context compilation is enabled, Assembly stdout/stderr are captured under each run directory as assembly.stdout.log and assembly.stderr.log.
./tests/run.sh./tests/run.sh now runs schema validation up front via the Rust validator binary. You can run schema validation directly with:
cargo run --quiet --bin validate_prd -- --tasks prd.json --schema prd.schema.jsonLever pins the Assembly CLI contract in docs/assembly-contract.md. You can validate a local Assembly installation with:
cargo run --quiet --bin validate_assembly_contract -- --assembly assemblyTasks must conform to prd.schema.json. The schema requires the following metadata for every task entry:
task_id: non-empty string that uniquely identifies the task.title: non-empty string summarizing the work.status: one of"unstarted","started","blocked", or"completed".model: one of"gpt-5.1-codex-mini","gpt-5.1-codex","gpt-5.2-codex", or"human".definition_of_done: non-empty array of non-empty strings describing completion criteria.recommended: object requiring anapproachstring (no other keys allowed).verification(optional): object with optionalcommandsarray of non-empty shell command strings. When present, these commands run (in order) as the deterministic verification step.
The optional observability object must appear only when there is recent run metadata, and it must include run_attempts (integer ≥ 0), last_note (string), last_update_utc (RFC 3339 / ISO 8601 string), and last_run_id (non-empty string).
The assignee property has been removed from the schema, so new tasks should no longer include it.
The Rust task agent now checks that every selected task exposes title, definition_of_done, and recommended.approach before starting a run. Missing metadata surface as an error, and the prompt includes each definition-of-done entry plus the recommended approach so the model can reason about the required behavior.
Minimal compliant example:
{
"task_id": "ASM-004",
"title": "Update README schema guidance",
"model": "gpt-5.1-codex-mini",
"status": "started",
"definition_of_done": [
"README describes every required task field",
"Example reflects current schema",
"Observability guidance notes required keys"
],
"recommended": {
"approach": "Keep the example minimal but complete and valid."
}
}