CLI
The lorenz binary is the only process you run. This page is the operator's task guide to the default daemon that polls a tracker and dispatches agents, lorenz runs to inspect run history, lorenz status/refresh/stop to control a running daemon, and lorenz doctor to validate a setup before you start it. For the flag-by-flag table, see reference/cli.md.
Commands
lorenz [flags] [WORKFLOW.md] # default: the daemon
lorenz runs [filters] # query run history from the observability API
lorenz status [WORKFLOW.md] # show the active daemon lease and endpoint
lorenz refresh [WORKFLOW.md] # ask the daemon to poll now
lorenz stop [WORKFLOW.md] # ask the daemon to stop gracefully
lorenz doctor [WORKFLOW.md] # validate a workflow and local prerequisites
runs, status, refresh, stop, and doctor are subcommands. Anything else runs the daemon. If the binary prints lorenz has not been built yet, run pnpm build first; the npm shim imports the built dist output.
Run the daemon
With no flags, lorenz loads a workflow, validates it, starts the observability server and the terminal dashboard, then polls until you stop it.
lorenz # poll forever, TUI + web dashboard on
lorenz WORKFLOW.md # same, with an explicit workflow path
Where the daemon finds your workflow
The workflow path resolves in this order:
- The positional
[WORKFLOW.md]argument, if given. - The
LORENZ_WORKFLOWenvironment variable. An absolute value is used as-is; a relative value is joined to the current directory. ./WORKFLOW.mdin the current directory.
A missing file fails fast with missing_workflow_file. The directory holding the workflow anchors relative paths inside it, including ./ worker-driver module specifiers.
Daemon flags
| Flag | Effect | Reach for it when |
|---|---|---|
--once |
Poll one tick, act on what is eligible, then exit. | Cron-style scheduling, or a one-shot sweep instead of a long-lived process. |
--dry-run |
Evaluate dispatch candidates and report what would run, without launching any agent. | Checking routing and eligibility against live tracker state before committing. |
--no-tui |
Disable the Ink terminal dashboard. | Logs, CI, or a non-interactive shell where the TUI is noise. |
--no-dashboard |
Disable the web dashboard and its HTTP API server. | You want no listening port at all. |
--port <port> |
Set the observability API port. Overrides server.port from the workflow. |
Pinning a known port, or avoiding a clash. 0 binds an ephemeral port. |
--logs-root <path> |
Write logs to <path>/log/lorenz.log instead of the configured logging.log_file. |
Redirecting logs to a writable scratch directory. |
--feature <name> |
Enable a feature bundle, e.g. daemon (long-lived daemon) or durable_claims (SQLite claim store). |
Turning on an opt-in capability. |
--flag <key=value> |
Set an individual flag, e.g. claim_store.backend=turso or claim_store.path=.... |
Tuning gated behavior without a feature bundle. |
--no-tui and --no-dashboard are the only forms; there is no positive --tui or --dashboard flag. Both surfaces are on by default. The TUI renders only when stdout is a TTY; without a TTY the runtime writes JSON snapshots to stdout on each update instead.
What startup does
runDaemon runs a fixed sequence:
- Register the built-in trackers, tool pack, agent executor, and worker drivers (idempotent).
- Load and parse the workflow, apply
--portand--logs-rootoverrides, and runvalidateDispatchConfig. - Acquire the same-host daemon lease when the
daemonfeature is enabled (long-running mode). The default (feature off) and--onceskip the lease. - Open the claim store selected by the
claim_store.backendflag. The default is in-memory; thedurable_claimsfeature selects SQLite. - Build the dispatch coordinator and warm worker pool. The pool is the single dispatch path, so it is always built; with no
worker.worker_poolblock it defaults to thelocaldriver (runs execute on the daemon host). - Run the
slots_per_machineblast-radius gate (see below). - Construct the runtime, start the observability server if enabled, then render the TUI or subscribe for JSON snapshots.
The runtime re-reads the workflow before every poll, so editing WORKFLOW.md while the daemon runs reloads it without a restart. A reload that fails to parse keeps the last good settings and records workflow_reload_failed. See workflow hot-reload.
When the server binds, the bound port is written back into server.port so reloads keep the same port, the daemon lease is updated with the HTTP control endpoint, and stderr prints Observability API listening on <url>. With --no-dashboard, no HTTP control endpoint is published.
Stopping it
A first Ctrl+C (SIGINT) or SIGTERM starts a graceful stop: the runtime finishes draining, the worker pool drains, the server stops, the issue store closes, the claim store closes, and the daemon lease is released. A second Ctrl+C while shutting down forces an exit with code 130.
Control commands
lorenz status, lorenz refresh, and lorenz stop discover the running daemon through the workflow's same-host lease. status reports the owner and endpoint. refresh and stop require the daemon's HTTP control endpoint, so they work when the dashboard server is enabled or when you pass --url/--port for a known endpoint.
The slots-per-machine gate
worker.worker_pool.slots_per_machine > 1 packs more than one run onto a single worker machine. The default 1 always passes. Above 1, the daemon refuses to start unless the coordinator advertises per-run claim enforcement and you have set worker.worker_pool.co_residence. The opt-in is required because one poisoned worker fails every co-resident run when it recycles. max_in_flight is the legacy alias for slots_per_machine.
Inspect runs
lorenz runs queries the daemon's observability API and prints run history. The daemon (or at least its dashboard server) must be running for this to return data.
lorenz runs # recent run history
lorenz runs --failed # only failed and stalled runs
lorenz runs --issue ENG-42 # runs for one issue
lorenz runs --id <runId> # one run plus its related attempts
lorenz runs --cost # token and cost summary by agent
lorenz runs --retries # retry summary by issue
lorenz runs --limit 50 # cap the number of runs returned
lorenz runs --json # raw JSON instead of tables
The filters map directly to query parameters on GET /api/v1/runs and select which view the server returns:
| Flag | View | Shows |
|---|---|---|
| (none) | runs |
Run history table plus totals by outcome. |
--failed |
runs |
Runs whose outcome is failed or stalled. |
--issue <id> |
runs |
Runs matching an issue identifier or id. |
--id <runId> |
run |
One run with session, worker, workspace, last event, failure reason, and related attempts. |
--cost |
cost |
Per-agent token totals and a top-runs table. |
--retries |
retries |
Attempts, latest outcome, and tokens per issue. |
--limit <n> |
(any) | Caps returned runs. The server defaults to 20 and clamps to 200. |
--json |
(any) | Prints the raw response body, skipping the table renderer. |
Dollar cost is not computed: those fields render as n/a, and the --cost view reports token totals.
Which server it talks to
The base URL resolves by precedence:
--url <url>if given. A trailing slash is trimmed. This wins even if--portis also passed.--port <port>with the workflow'sserver.host, when the port is greater than0.server.portfrom the workflow, withserver.host, when it is greater than0.
Port 0 counts as no explicit port. If none of these yield a port, the command fails with No observability server port configured. Pass --port/--url or set server.port in WORKFLOW.md.
The command maps HTTP status codes to messages: 404 prints Run not found, 503 prints Observability API unavailable, and anything else prints Unexpected response status <N>.
Validate with doctor
lorenz doctor loads a workflow and checks your local setup without dispatching anything. It exits 1 only when a check errors; warnings keep the exit code at 0.
lorenz doctor # validate ./WORKFLOW.md and prerequisites
lorenz doctor WORKFLOW.md # explicit path
lorenz doctor --no-dashboard # skip the static-asset check
Doctor uses the same path resolution as the daemon. It runs these checks in order and short-circuits on the first hard error:
| Check id | Meaning | Failure mode |
|---|---|---|
workflow_file |
The workflow path exists, is a file, and is readable. | error (stops here). |
workflow_load |
The workflow parses into valid settings. | error (stops here). |
config_deprecations / config_deprecation_* |
No deprecated config keys are in use (legacy top-level codex:/claude: sections, flat-shape tracker provider options). One config_deprecation_<key> check per deprecated key names its replacement. |
warning. |
dispatch_config |
validateDispatchConfig passes against the built-in tracker, executor, and tool registries. |
error. |
dashboard_assets |
The built dashboard SPA (server.staticDir, else the default dist) is present. |
warning. |
log_path |
The nearest existing ancestor of logging.log_file exists and is writable. |
warning. |
agent_bridge_* |
The ACP bridge command for each active and per-state agent is parseable and on PATH. |
warning. |
agent_cli_* |
The underlying agent CLI behind each bridge is discoverable. | warning. |
The overall status is error if any check errors, otherwise warning if any warns, otherwise ok.
Bridge checks run only for the acp executor. They inspect the active config and every status_overrides state. With remote workers (worker.ssh_hosts set), doctor skips bridge probing and emits a warning instead of reaching over SSH. The CLI check resolves claude for Claude-compatible bridges (overridable with CLAUDE_CODE_EXECUTABLE) and codex for codex-acp bridges (overridable with CODEX_PATH).
Environment variables
| Variable | Effect |
|---|---|
LORENZ_WORKFLOW |
Workflow file path. Absolute is used as-is; relative joins the current directory. |
LORENZ_WORKSPACE_ROOT |
Overrides workspace.root. |
LORENZ_SSH_CONFIG |
Path passed to ssh -F for remote workers. |
CLAUDE_CODE_EXECUTABLE |
Overrides the claude binary path used by doctor and the ACP bridge. |
CODEX_PATH |
Overrides the codex binary path used by doctor and the ACP bridge. |
Tracker credentials resolve through the workflow config, not generic CLI flags. See secret resolution.
See also
- reference/cli.md - the exhaustive flag, argument, and exit-code reference
- getting-started.md - first workflow and first run end to end
- workflows.md - what goes in
WORKFLOW.mdand how it is parsed - observability.md - the dashboard, HTTP API, and TUI the daemon serves
- features/run-history.md - what
lorenz runsis querying - troubleshooting.md - when a run stalls, fails, or will not start