Lorenz Docs

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:

  1. The positional [WORKFLOW.md] argument, if given.
  2. The LORENZ_WORKFLOW environment variable. An absolute value is used as-is; a relative value is joined to the current directory.
  3. ./WORKFLOW.md in 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:

  1. Register the built-in trackers, tool pack, agent executor, and worker drivers (idempotent).
  2. Load and parse the workflow, apply --port and --logs-root overrides, and run validateDispatchConfig.
  3. Acquire the same-host daemon lease when the daemon feature is enabled (long-running mode). The default (feature off) and --once skip the lease.
  4. Open the claim store selected by the claim_store.backend flag. The default is in-memory; the durable_claims feature selects SQLite.
  5. Build the dispatch coordinator and warm worker pool. The pool is the single dispatch path, so it is always built; with no worker.worker_pool block it defaults to the local driver (runs execute on the daemon host).
  6. Run the slots_per_machine blast-radius gate (see below).
  7. 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:

  1. --url <url> if given. A trailing slash is trimmed. This wins even if --port is also passed.
  2. --port <port> with the workflow's server.host, when the port is greater than 0.
  3. server.port from the workflow, with server.host, when it is greater than 0.

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

© 2026 Lorenz Issues in, agent runs out.