lorenz 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +75 -738
- package/RELEASE-MANIFEST.json +1 -1
- package/apps/cli/dist/doctor.d.ts.map +1 -1
- package/apps/cli/dist/doctor.js +20 -1
- package/apps/cli/dist/doctor.js.map +1 -1
- package/apps/cli/dist/main.d.ts.map +1 -1
- package/apps/cli/dist/main.js +10 -1
- package/apps/cli/dist/main.js.map +1 -1
- package/extensions/docker-worker/package.json +1 -1
- package/extensions/jira-tracker/package.json +1 -1
- package/extensions/linear-tracker/package.json +1 -1
- package/extensions/local-tracker/package.json +1 -1
- package/extensions/memory-tracker/package.json +1 -1
- package/extensions/slack-tracker/dist/client.d.ts +13 -2
- package/extensions/slack-tracker/dist/client.d.ts.map +1 -1
- package/extensions/slack-tracker/dist/client.js +28 -2
- package/extensions/slack-tracker/dist/client.js.map +1 -1
- package/extensions/slack-tracker/dist/inMemoryTransport.d.ts +4 -1
- package/extensions/slack-tracker/dist/inMemoryTransport.d.ts.map +1 -1
- package/extensions/slack-tracker/dist/inMemoryTransport.js +4 -2
- package/extensions/slack-tracker/dist/inMemoryTransport.js.map +1 -1
- package/extensions/slack-tracker/dist/index.d.ts +3 -1
- package/extensions/slack-tracker/dist/index.d.ts.map +1 -1
- package/extensions/slack-tracker/dist/index.js +2 -1
- package/extensions/slack-tracker/dist/index.js.map +1 -1
- package/extensions/slack-tracker/dist/mapping.d.ts +9 -0
- package/extensions/slack-tracker/dist/mapping.d.ts.map +1 -1
- package/extensions/slack-tracker/dist/mapping.js +13 -0
- package/extensions/slack-tracker/dist/mapping.js.map +1 -1
- package/extensions/slack-tracker/dist/operations.d.ts.map +1 -1
- package/extensions/slack-tracker/dist/operations.js +8 -4
- package/extensions/slack-tracker/dist/operations.js.map +1 -1
- package/extensions/slack-tracker/dist/options.d.ts +21 -1
- package/extensions/slack-tracker/dist/options.d.ts.map +1 -1
- package/extensions/slack-tracker/dist/options.js +3 -0
- package/extensions/slack-tracker/dist/options.js.map +1 -1
- package/extensions/slack-tracker/dist/provider.d.ts.map +1 -1
- package/extensions/slack-tracker/dist/provider.js +21 -1
- package/extensions/slack-tracker/dist/provider.js.map +1 -1
- package/extensions/slack-tracker/dist/socketMode.d.ts +81 -0
- package/extensions/slack-tracker/dist/socketMode.d.ts.map +1 -0
- package/extensions/slack-tracker/dist/socketMode.js +226 -0
- package/extensions/slack-tracker/dist/socketMode.js.map +1 -0
- package/extensions/slack-tracker/dist/threadState.d.ts.map +1 -1
- package/extensions/slack-tracker/dist/threadState.js +8 -4
- package/extensions/slack-tracker/dist/threadState.js.map +1 -1
- package/extensions/slack-tracker/dist/webTransport.d.ts +1 -0
- package/extensions/slack-tracker/dist/webTransport.d.ts.map +1 -1
- package/extensions/slack-tracker/dist/webTransport.js +10 -4
- package/extensions/slack-tracker/dist/webTransport.js.map +1 -1
- package/extensions/slack-tracker/package.json +1 -1
- package/package.json +1 -2
- package/packages/acp/package.json +1 -1
- package/packages/agent-runner/package.json +1 -1
- package/packages/agent-sdk/package.json +1 -1
- package/packages/cli-kit/package.json +1 -1
- package/packages/config/dist/deprecations.d.ts +13 -0
- package/packages/config/dist/deprecations.d.ts.map +1 -0
- package/packages/config/dist/deprecations.js +25 -0
- package/packages/config/dist/deprecations.js.map +1 -0
- package/packages/config/dist/index.d.ts +3 -0
- package/packages/config/dist/index.d.ts.map +1 -1
- package/packages/config/dist/index.js +1 -0
- package/packages/config/dist/index.js.map +1 -1
- package/packages/config/dist/parse.d.ts +11 -1
- package/packages/config/dist/parse.d.ts.map +1 -1
- package/packages/config/dist/parse.js +6 -1
- package/packages/config/dist/parse.js.map +1 -1
- package/packages/config/dist/schemas.d.ts +15 -0
- package/packages/config/dist/schemas.d.ts.map +1 -1
- package/packages/config/dist/schemas.js +127 -14
- package/packages/config/dist/schemas.js.map +1 -1
- package/packages/config/package.json +1 -1
- package/packages/dispatch/package.json +1 -1
- package/packages/dispatch-coordinator/package.json +1 -1
- package/packages/domain/dist/index.d.ts +22 -0
- package/packages/domain/dist/index.d.ts.map +1 -1
- package/packages/domain/dist/index.js.map +1 -1
- package/packages/domain/package.json +1 -1
- package/packages/humanize/package.json +1 -1
- package/packages/issue/package.json +1 -1
- package/packages/log-file/package.json +1 -1
- package/packages/mcp/package.json +1 -1
- package/packages/orchestrator/package.json +1 -1
- package/packages/policies/package.json +1 -1
- package/packages/presenter/package.json +1 -1
- package/packages/projections/package.json +1 -1
- package/packages/prompt/package.json +1 -1
- package/packages/retry-scheduler/package.json +1 -1
- package/packages/runtime/dist/index.d.ts +24 -0
- package/packages/runtime/dist/index.d.ts.map +1 -1
- package/packages/runtime/dist/index.js +78 -0
- package/packages/runtime/dist/index.js.map +1 -1
- package/packages/runtime/package.json +1 -1
- package/packages/runtime-events/dist/index.d.ts +1 -1
- package/packages/runtime-events/dist/index.d.ts.map +1 -1
- package/packages/runtime-events/dist/index.js +3 -0
- package/packages/runtime-events/dist/index.js.map +1 -1
- package/packages/runtime-events/package.json +1 -1
- package/packages/server/package.json +1 -1
- package/packages/ssh/package.json +1 -1
- package/packages/static-worker/package.json +1 -1
- package/packages/tool-sdk/package.json +1 -1
- package/packages/traceviz-emitter/package.json +1 -1
- package/packages/traceviz-server/package.json +1 -1
- package/packages/tracker-sdk/package.json +1 -1
- package/packages/tui/package.json +1 -1
- package/packages/worker-host-pool/package.json +1 -1
- package/packages/worker-pool/package.json +1 -1
- package/packages/worker-sdk/package.json +1 -1
- package/packages/workflow/package.json +1 -1
- package/packages/workspace/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,18 +1,13 @@
|
|
|
1
1
|
# Lorenz
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://ryanlyn.github.io/lorenz/)
|
|
4
4
|
|
|
5
|
-
Lorenz
|
|
6
|
-
renders the workflow prompt with issue context, starts Codex or Claude, and records the run so
|
|
7
|
-
operators can inspect state, retries, cost, and logs.
|
|
8
|
-
|
|
9
|
-
This repository owns the TypeScript CLI, runtime packages, tracker adapters, terminal dashboard,
|
|
10
|
-
local observability server, trace viewer packages, and tests.
|
|
5
|
+
Originated from [OpenAI Symphony](https://openai.com/index/open-source-codex-orchestration-symphony/), Lorenz lets you declare work on trackers (in-memory, Obsidian markdown files, Linear, Jira, Slack etc.) and manage the dispatch, execution, and convergence of concurrent agent sessions until they reach a specified terminal state. It is harness-agnostic through the [Agent-Client Protocol](https://agentclientprotocol.com/get-started/introduction) with support for local, static SSH boxes, or (experimental) cloud-brokered VMs.
|
|
11
6
|
|
|
12
7
|
## Screenshots
|
|
13
8
|
|
|
14
|
-
Lorenz ships two operator views over the same runtime snapshot: an Ink terminal
|
|
15
|
-
|
|
9
|
+
Lorenz ships two operator views over the same runtime snapshot: an Ink terminal dashboard (TUI)
|
|
10
|
+
and a web dashboard served by the observability API.
|
|
16
11
|
|
|
17
12
|
### Terminal dashboard (TUI)
|
|
18
13
|
|
|
@@ -22,734 +17,109 @@ dashboard (TUI) and a web dashboard served by the observability API.
|
|
|
22
17
|
|
|
23
18
|

|
|
24
19
|
|
|
25
|
-
##
|
|
26
|
-
|
|
27
|
-
1. Polls Linear for issues in active states (e.g. `Todo`, `In Progress`)
|
|
28
|
-
2. Creates a workspace per issue and bootstraps it via `hooks.after_create`
|
|
29
|
-
3. Launches the configured agent executor (Codex or Claude Code) inside the workspace
|
|
30
|
-
4. Renders a Liquid-templated prompt from `WORKFLOW.md` with issue context and sends it to the agent
|
|
31
|
-
5. Re-runs the agent on subsequent polling cycles if the issue remains active, up to `max_turns`
|
|
32
|
-
6. When an issue moves to a terminal state (`Done`, `Closed`, `Cancelled`, `Duplicate`), stops the
|
|
33
|
-
agent and cleans up the workspace
|
|
20
|
+
## Documentation
|
|
34
21
|
|
|
35
|
-
The
|
|
36
|
-
the agent session prompt (Markdown body). Editing the workflow while Lorenz is running reloads the
|
|
37
|
-
configuration automatically - no restart needed.
|
|
22
|
+
The published documentation site is at **[ryanlyn.github.io/lorenz](https://ryanlyn.github.io/lorenz/)**, built from [`docs/`](./docs). Start here:
|
|
38
23
|
|
|
39
|
-
|
|
24
|
+
- [Getting started](./docs/getting-started.md) - install, write a `WORKFLOW.md`, run your first issue.
|
|
25
|
+
- [How it works](./docs/how-it-works.md) - the polling, dispatch, and run lifecycle.
|
|
26
|
+
- [Configuration reference](./docs/reference/configuration.md) - every front-matter key, default, and meaning.
|
|
27
|
+
- [Trackers](./docs/trackers/index.md) - Linear, Jira, Slack, local, and memory sources of issues.
|
|
28
|
+
- [CLI](./docs/cli.md) - commands, flags, and run history.
|
|
40
29
|
|
|
41
|
-
|
|
42
|
-
| ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
43
|
-
| Context Ensembles | Adds configurable multi-agent issue fan-out with per-slot workspaces, prompt/dashboard ensemble context, `ensemble:*` label overrides, and a dedicated `WORKFLOW_ENSEMBLE.md` example for independent workpads. |
|
|
44
|
-
| Claude Code executor | Adds `agent.kind: "claude"` support, including Claude CLI execution, JSONL event parsing, built-in `/mcp` tool serving instead of the Python MCP sidecar, authenticated remote worker access, and Claude-specific runtime settings. |
|
|
45
|
-
| Workflow and runtime hardening | Defaults Codex workflows to sandboxed `workspace-write`, honors Linear `Retry-After` backoff on `429`, tightens remote workspace path validation, and improves long-running orchestrator reliability. |
|
|
46
|
-
| Claude parity and MCP handling | Routes Claude and Codex through the same Linear tool backend, removes the Python MCP sidecar, and improves remote cleanup behavior. |
|
|
47
|
-
| Dispatch routing | Adds tracker-scoped static routing with Linear labels such as `Lorenz:shard-a`, so multiple Lorenz instances can split work by configured route labels. |
|
|
48
|
-
| Run history CLI | Adds an orchestrator run history command (`lorenz runs`) exposing completed attempts, retries, token totals, and per-run forensic context beyond live state. |
|
|
49
|
-
| Secret resolution | Resolves `op://` references in workflow secrets (e.g. `LINEAR_API_KEY`) through the 1Password CLI. |
|
|
30
|
+
## Quickstart
|
|
50
31
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
[mise](https://mise.jdx.dev/) is recommended for managing Node and pnpm:
|
|
32
|
+
Running Lorenz is as easy as:
|
|
54
33
|
|
|
55
34
|
```sh
|
|
56
|
-
|
|
57
|
-
mise install
|
|
58
|
-
pnpm install
|
|
35
|
+
npx lorenz WORKFLOW.md
|
|
59
36
|
```
|
|
60
37
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
Runtime requirements depend on the workflow:
|
|
64
|
-
|
|
65
|
-
- `LINEAR_API_KEY` for Linear-backed workflows.
|
|
66
|
-
- `codex` on `PATH` for Codex runs and live Codex tests.
|
|
67
|
-
- A Claude ACP bridge, usually `claude-agent-acp`, for Claude runs and live Claude tests.
|
|
68
|
-
- SSH access for remote workers and live SSH tests.
|
|
69
|
-
- Docker and `ssh-keygen` for disposable live SSH workers when no real SSH hosts are configured.
|
|
70
|
-
|
|
71
|
-
Run commands from the repository root unless a command says otherwise.
|
|
72
|
-
|
|
73
|
-
## Run
|
|
74
|
-
|
|
75
|
-
```sh
|
|
76
|
-
pnpm build
|
|
77
|
-
pnpm start -- WORKFLOW.md
|
|
78
|
-
pnpm start:once -- --dry-run --no-tui WORKFLOW.md
|
|
79
|
-
pnpm runs -- --port 4000 --failed
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
The built CLI is `lorenz`:
|
|
38
|
+
with full CLI options:
|
|
83
39
|
|
|
84
40
|
```sh
|
|
85
41
|
lorenz [--once] [--dry-run] [--no-tui] [--port <port>] [--logs-root <path>] [path-to-WORKFLOW.md]
|
|
86
|
-
lorenz runs [--issue ID] [--failed] [--cost] [--retries] [--id RUN_ID] [--limit N] [--
|
|
42
|
+
lorenz runs [--issue ID] [--failed] [--cost] [--retries] [--id RUN_ID] [--limit N] [--json]
|
|
87
43
|
```
|
|
88
44
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
With no workflow path, the CLI reads `LORENZ_WORKFLOW`, then `./WORKFLOW.md`.
|
|
98
|
-
|
|
99
|
-
The runtime reloads the workflow before each poll. If startup cannot read or parse the workflow,
|
|
100
|
-
the CLI exits with an error. If a later reload fails, the runtime keeps the last good workflow and
|
|
101
|
-
records a `workflow_reload_failed` event.
|
|
102
|
-
|
|
103
|
-
## Workspace Layout
|
|
104
|
-
|
|
105
|
-
See [docs/ARCHITECTURE.md](./docs/ARCHITECTURE.md) for the layering rules and the tracker extension
|
|
106
|
-
contract (including the recipe for adding a new tracker backend).
|
|
107
|
-
|
|
108
|
-
- `apps/cli` is the composition root: it invokes the built-in extensions' registration and wires
|
|
109
|
-
configuration, agent runners, the runtime, the TUI, and the observability server into the
|
|
110
|
-
shipped binary.
|
|
111
|
-
- `apps/traceviz` renders trace event streams for local inspection.
|
|
112
|
-
- `packages/tracker-sdk` is the extension SDK: the `TrackerProvider` contract, the provider
|
|
113
|
-
registry, and the helpers tracker backends build on.
|
|
114
|
-
- `extensions/*` are the backend extensions: `linear-tracker`, `local-tracker`,
|
|
115
|
-
`memory-tracker`, and `jira-tracker` are self-contained tracker providers (config
|
|
116
|
-
parsing, runtime client, tool packs) that each export their own registration; the CLI
|
|
117
|
-
invokes the built-in set at its composition root.
|
|
118
|
-
- The remaining `packages/*` are the provider-agnostic engine: domain model, configuration
|
|
119
|
-
loader, prompt renderer, runtime, policies, MCP server, dashboards, logging, SSH, and
|
|
120
|
-
support libraries.
|
|
121
|
-
- `test/` contains workspace-level integration, contract, sandbox, and live tests.
|
|
122
|
-
- Package- and app-owned unit tests live under `packages/<name>/test/` or `apps/<name>/test/` as
|
|
123
|
-
`.test.ts` or `.test.tsx` files.
|
|
124
|
-
|
|
125
|
-
Create a package when a boundary has a clear owner. Keep curated exports in `src/index.ts` and
|
|
126
|
-
declare internal dependencies as `workspace:*`.
|
|
45
|
+
`--logs-root <path>` writes logs under `<path>/log/lorenz.log`. With no workflow path the CLI reads
|
|
46
|
+
`LORENZ_WORKFLOW`, then `./WORKFLOW.md`. See [CLI](./docs/cli.md) for every flag and command.
|
|
47
|
+
|
|
48
|
+
Runtime needs depend on the workflow: `LINEAR_API_KEY` for Linear, `codex` on `PATH` for Codex
|
|
49
|
+
runs, a Claude ACP bridge for Claude runs, and SSH access for remote workers. See
|
|
50
|
+
[Getting started](./docs/getting-started.md) for the full list. Run commands from the repository
|
|
51
|
+
root unless a command says otherwise.
|
|
127
52
|
|
|
128
53
|
## Configuration
|
|
129
54
|
|
|
130
55
|
Configuration lives in the YAML front matter of a workflow file. The Markdown body below the front
|
|
131
|
-
matter is the agent session prompt, rendered as Liquid with issue
|
|
132
|
-
|
|
133
|
-
### Quickstart
|
|
134
|
-
|
|
135
|
-
```yaml
|
|
136
|
-
---
|
|
137
|
-
tracker:
|
|
138
|
-
kind: linear
|
|
139
|
-
trackers:
|
|
140
|
-
linear:
|
|
141
|
-
provider: linear
|
|
142
|
-
project_slug: "your-project-slug"
|
|
143
|
-
workspace:
|
|
144
|
-
root: ~/code/workspaces
|
|
145
|
-
hooks:
|
|
146
|
-
after_create: |
|
|
147
|
-
git clone git@github.com:your-org/your-repo.git .
|
|
148
|
-
agent:
|
|
149
|
-
kind: codex
|
|
150
|
-
---
|
|
151
|
-
|
|
152
|
-
You are working on {{ issue.identifier }}: {{ issue.title }}
|
|
153
|
-
|
|
154
|
-
{{ issue.description }}
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
Set `LINEAR_API_KEY` in your environment before running a Linear workflow.
|
|
56
|
+
matter is the agent session prompt, rendered as Liquid with issue-context variables. See
|
|
57
|
+
[Workflows](./docs/workflows.md) for the file format and a quickstart example.
|
|
158
58
|
|
|
159
59
|
### Full Reference
|
|
160
60
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
kind: linear # linear, jira, jira-mcp, local, or memory
|
|
165
|
-
trackers:
|
|
166
|
-
linear:
|
|
167
|
-
provider: linear
|
|
168
|
-
api_key: $LINEAR_API_KEY # defaults to $LINEAR_API_KEY when unset
|
|
169
|
-
endpoint: "https://api.linear.app/graphql"
|
|
170
|
-
project_slug: "my-project" # right-click a Linear project and copy the URL slug
|
|
171
|
-
assignee: $LINEAR_ASSIGNEE # optional; filters issues by assignee
|
|
172
|
-
active_states:
|
|
173
|
-
- Todo # default: ["Todo", "In Progress"]
|
|
174
|
-
- In Progress
|
|
175
|
-
- Agent Review
|
|
176
|
-
- Merging
|
|
177
|
-
- Rework
|
|
178
|
-
terminal_states:
|
|
179
|
-
- Closed # default: ["Closed", "Cancelled", "Canceled",
|
|
180
|
-
- Cancelled # "Duplicate", "Done"]
|
|
181
|
-
- Canceled
|
|
182
|
-
- Duplicate
|
|
183
|
-
- Done
|
|
184
|
-
dispatch:
|
|
185
|
-
accept_unrouted: true # accept issues without a route label; default: true
|
|
186
|
-
only_routes: null # null accepts any route, [] accepts none
|
|
187
|
-
route_label_prefix: "Lorenz:" # route labels look like "Lorenz:backend"
|
|
188
|
-
|
|
189
|
-
tools:
|
|
190
|
-
local:
|
|
191
|
-
path: .lorenz/local # explicit extra pack config; not needed for Linear-owned tools
|
|
192
|
-
|
|
193
|
-
polling:
|
|
194
|
-
interval_ms: 30000 # default: 30000
|
|
195
|
-
|
|
196
|
-
workspace:
|
|
197
|
-
root: ~/code/workspaces # default: $TMPDIR/lorenz_workspaces
|
|
198
|
-
|
|
199
|
-
worker:
|
|
200
|
-
# Either list static SSH hosts here, or set kind to select a top-level workers.<name> profile.
|
|
201
|
-
# kind: static-prod
|
|
202
|
-
ssh_hosts:
|
|
203
|
-
- worker1.example.com # standard OpenSSH targets and Host aliases work
|
|
204
|
-
- worker2.example.com:2222
|
|
205
|
-
ssh_timeout_ms: 60000 # default: 60000
|
|
206
|
-
max_concurrent_agents_per_host: 2 # optional; defaults to the global agent cap per host
|
|
207
|
-
# Alternative to ssh_hosts (mutually exclusive): a warm pool of leased workers
|
|
208
|
-
# provisioned by a worker driver. Disabled by default.
|
|
209
|
-
worker_pool:
|
|
210
|
-
enabled: false
|
|
211
|
-
# driver: fake # compatibility fallback when worker.kind is omitted
|
|
212
|
-
min: 0 # warm-inventory floor the reaper keeps alive
|
|
213
|
-
max: 1 # ceiling on concurrent workers
|
|
214
|
-
warm: 1 # pre-warmed idle workers the reaper tops up toward
|
|
215
|
-
max_in_flight: 1 # run slots per machine; >1 requires co_residence: true
|
|
216
|
-
ttl_ms: 3600000 # hard worker lifetime before recycle
|
|
217
|
-
idle_reap_ms: 300000 # idle window before a warm worker above min is reaped
|
|
218
|
-
acquire_timeout_ms: 30000 # how long an acquire waits for capacity
|
|
219
|
-
spend: # optional caps, all in worker count / wall-clock worker-seconds
|
|
220
|
-
max_concurrent_workers: 4
|
|
221
|
-
max_worker_seconds: 86400
|
|
222
|
-
daily_worker_seconds: 28800
|
|
223
|
-
|
|
224
|
-
workers:
|
|
225
|
-
static-prod: # selected by worker.kind, options pass through verbatim (snake_case preserved)
|
|
226
|
-
driver: static-ssh
|
|
227
|
-
ssh_hosts: ["user@worker1:22"]
|
|
228
|
-
|
|
229
|
-
agent:
|
|
230
|
-
kind: codex # default: "codex"; "claude" is configured below
|
|
231
|
-
max_concurrent_agents: 10 # default: 10
|
|
232
|
-
max_turns: 20 # default: 20
|
|
233
|
-
max_retry_backoff_ms: 300000 # default: 300000
|
|
234
|
-
ensemble_size: 1 # default: 1
|
|
235
|
-
skills: # skill directories copied to .lorenz/skills/ before the agent starts
|
|
236
|
-
- ./skills/lorenz-land # one entry per skill directory
|
|
237
|
-
|
|
238
|
-
agents:
|
|
239
|
-
turn_timeout_ms: 3600000 # default: 3600000
|
|
240
|
-
stall_timeout_ms: 300000 # default: 300000
|
|
241
|
-
codex:
|
|
242
|
-
executor: acp
|
|
243
|
-
bridge_command: codex-acp
|
|
244
|
-
claude:
|
|
245
|
-
executor: acp
|
|
246
|
-
bridge_command: claude-agent-acp
|
|
247
|
-
bridge_args:
|
|
248
|
-
- --permission-mode
|
|
249
|
-
- dontAsk
|
|
250
|
-
- --model
|
|
251
|
-
- claude-opus-4-6[1m]
|
|
252
|
-
|
|
253
|
-
status_overrides:
|
|
254
|
-
in progress:
|
|
255
|
-
agent:
|
|
256
|
-
max_concurrent_agents: 5
|
|
257
|
-
merging:
|
|
258
|
-
agent:
|
|
259
|
-
max_concurrent_agents: 2
|
|
260
|
-
|
|
261
|
-
codex:
|
|
262
|
-
command: codex-acp # legacy alias for agents.codex.bridge_command
|
|
263
|
-
turn_timeout_ms: 3600000 # default: 3600000
|
|
264
|
-
stall_timeout_ms: 300000 # default: 300000
|
|
265
|
-
|
|
266
|
-
claude:
|
|
267
|
-
command: claude-agent-acp # ACP bridge command
|
|
268
|
-
model: claude-opus-4-6[1m]
|
|
269
|
-
permission_mode: dontAsk
|
|
270
|
-
strict_mcp_config: true # default: true
|
|
271
|
-
|
|
272
|
-
hooks:
|
|
273
|
-
after_create: | # runs after a workspace directory is created
|
|
274
|
-
git clone --depth 1 git@github.com:org/repo.git .
|
|
275
|
-
before_run: | # runs before each agent turn
|
|
276
|
-
git pull origin main
|
|
277
|
-
after_run: | # best effort; runs after each agent turn
|
|
278
|
-
echo "turn complete"
|
|
279
|
-
before_remove: | # best effort; runs before workspace cleanup
|
|
280
|
-
echo "cleaning up"
|
|
281
|
-
timeout_ms: 60000 # default: 60000
|
|
282
|
-
|
|
283
|
-
observability:
|
|
284
|
-
dashboard_enabled: true # terminal dashboard; default: true
|
|
285
|
-
refresh_ms: 1000 # default: 1000
|
|
286
|
-
render_interval_ms: 16 # default: 16
|
|
287
|
-
|
|
288
|
-
server:
|
|
289
|
-
port: 4000 # enables the web dashboard; default: disabled
|
|
290
|
-
host: 127.0.0.1 # default: 127.0.0.1
|
|
291
|
-
|
|
292
|
-
logging:
|
|
293
|
-
log_file: ./log/lorenz.log # default: ~/.lorenz/log/lorenz.log
|
|
294
|
-
---
|
|
295
|
-
```
|
|
296
|
-
|
|
297
|
-
Notes:
|
|
298
|
-
|
|
299
|
-
- `tracker.kind` is always required. When `trackers` is present it selects a named bundle, and
|
|
300
|
-
`trackers.<name>.provider` selects the tracker implementation.
|
|
301
|
-
- The older flat `tracker.kind: linear` shape is still accepted when no `trackers` map is present.
|
|
302
|
-
- `trackers.linear.project_slug`, `trackers.linear.project_slugs`, or
|
|
303
|
-
`trackers.linear.project_labels` is required for Linear workflows.
|
|
304
|
-
- `trackers.linear.api_key` falls back to `LINEAR_API_KEY`; `trackers.linear.assignee` falls back
|
|
305
|
-
to `LINEAR_ASSIGNEE`.
|
|
306
|
-
- Shared tracker secrets can use `op://` references when the 1Password CLI is installed.
|
|
307
|
-
- `tools.<pack>` mounts or configures extra tool packs. Tracker-owned tools are implicit, so a
|
|
308
|
-
Linear tracker does not need a matching `tools.linear` entry.
|
|
309
|
-
- `workspace.root` supports `~` and whole-value `$VAR` expansion. `LORENZ_WORKSPACE_ROOT`
|
|
310
|
-
overrides `workspace.root` at runtime.
|
|
311
|
-
- `LORENZ_SSH_CONFIG` points SSH worker commands at a custom OpenSSH config file.
|
|
312
|
-
- Hooks run through `bash -lc` locally or over SSH with the workspace as `cwd`. Use
|
|
313
|
-
fail-fast shell options in bootstrap hooks so clone and dependency setup failures stop workspace
|
|
314
|
-
creation immediately.
|
|
315
|
-
- `codex.command` runs through `bash -lc`, so shell expansion happens in the launched process.
|
|
316
|
-
- If the Markdown body is blank, Lorenz uses a default prompt with the issue identifier, title,
|
|
317
|
-
and body.
|
|
61
|
+
Every front-matter key, its type, verified default, and meaning are in the
|
|
62
|
+
[Configuration reference](./docs/reference/configuration.md). `workspace.root` supports `~` and
|
|
63
|
+
whole-value `$VAR` expansion, and `LORENZ_WORKSPACE_ROOT` overrides it at runtime.
|
|
318
64
|
|
|
319
65
|
## Linear
|
|
320
66
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
path.
|
|
327
|
-
4. The example workflows use non-standard states such as `Agent Review`, `Rework`, `Human Review`,
|
|
328
|
-
and `Merging`. Add those states under Team Settings, Workflow, or adjust `active_states` and
|
|
329
|
-
`terminal_states` to match your team.
|
|
330
|
-
|
|
331
|
-
Route labels let multiple Lorenz instances share one Linear project. With the default
|
|
332
|
-
`route_label_prefix`, labels such as `Lorenz:backend` and `Lorenz:frontend` become route names.
|
|
333
|
-
|
|
334
|
-
## Trackers
|
|
335
|
-
|
|
336
|
-
A tracker is the source of issues Lorenz works on. `tracker.kind` selects a named bundle under
|
|
337
|
-
`trackers`, and the selected bundle's `provider` selects the implementation. Every tracker exposes
|
|
338
|
-
the same read surface to the runtime (poll for candidate issues, refresh in-flight issues by id)
|
|
339
|
-
and a set of agent tools. Those tools are read+write symmetric across kinds, mirroring
|
|
340
|
-
`linear_graphql` (which both reads and writes): each tracker gives the agent at least one write tool
|
|
341
|
-
and one read tool. The tools differ per kind; their descriptions are self-documenting and surface
|
|
342
|
-
to the agent via the MCP `tools/list` call.
|
|
343
|
-
|
|
344
|
-
Supported kinds:
|
|
345
|
-
|
|
346
|
-
- `linear` - issues live in a Linear project. Read access uses `trackers.linear.api_key` (resolved
|
|
347
|
-
from `LINEAR_API_KEY`) and project selection uses `trackers.linear.project_slug`,
|
|
348
|
-
`trackers.linear.project_slugs`, or `trackers.linear.project_labels`. Agents can use
|
|
349
|
-
provider-neutral `tracker_*` tools or the legacy
|
|
350
|
-
`linear_graphql` tool.
|
|
351
|
-
- `jira` - issues live in Jira Cloud and are accessed directly over Jira REST. Configure
|
|
352
|
-
`trackers.jira.base_url`, `trackers.jira.email`, `trackers.jira.api_key`, and either
|
|
353
|
-
`trackers.jira.project_keys` or `trackers.jira.jql`. `JIRA_BASE_URL`, `JIRA_EMAIL`, and
|
|
354
|
-
`JIRA_API_KEY` are used as fallbacks.
|
|
355
|
-
- `jira-mcp` - issues live in Jira, but Lorenz reaches them through an external MCP server.
|
|
356
|
-
Configure `trackers.jira-mcp.mcp.url` and either `trackers.jira-mcp.project_keys` or
|
|
357
|
-
`trackers.jira-mcp.jql`. Tool names can be overridden under `trackers.jira-mcp.mcp.tools`.
|
|
358
|
-
- `local` - issues live as Markdown files on disk. No external service required.
|
|
359
|
-
- `slack` - an @-mention of the bot (in a channel message or a thread reply) is an issue, the
|
|
360
|
-
thread carries the status (`@bot !` commands and bot `status:` replies), and a thread reply is
|
|
361
|
-
a comment.
|
|
362
|
-
- `memory` - an in-process tracker used for tests and dry runs.
|
|
363
|
-
|
|
364
|
-
All non-memory providers expose the provider-neutral agent tools:
|
|
365
|
-
|
|
366
|
-
- `tracker_read_issue`
|
|
367
|
-
- `tracker_query`
|
|
368
|
-
- `tracker_update_status`
|
|
369
|
-
- `tracker_list_comments`
|
|
370
|
-
- `tracker_comment`
|
|
371
|
-
- `tracker_update_comment`
|
|
372
|
-
- `tracker_create_issue`
|
|
373
|
-
|
|
374
|
-
Provider-specific tools are compatibility escape hatches, not the preferred workflow contract.
|
|
375
|
-
|
|
376
|
-
All kinds share the dispatch routing block under the selected tracker bundle:
|
|
377
|
-
|
|
378
|
-
```yaml
|
|
379
|
-
tracker:
|
|
380
|
-
kind: linear
|
|
381
|
-
trackers:
|
|
382
|
-
linear:
|
|
383
|
-
provider: linear
|
|
384
|
-
dispatch:
|
|
385
|
-
accept_unrouted: true # process issues that carry no matching route label (default)
|
|
386
|
-
only_routes: null # or a list of route names this instance handles
|
|
387
|
-
route_label_prefix: "Lorenz:" # the label prefix that names a route
|
|
388
|
-
```
|
|
389
|
-
|
|
390
|
-
### Jira tracker
|
|
391
|
-
|
|
392
|
-
For both `jira` and `jira-mcp`, Lorenz only picks up issues that are assigned to the configured
|
|
393
|
-
user (`trackers.jira.assignee` or `trackers.jira-mcp.assignee`, defaulting to the authenticated
|
|
394
|
-
user via `assignee = currentUser()`) and labeled `agent`. This holds even when the configured JQL
|
|
395
|
-
widens the scope, so issues must be explicitly delegated before Lorenz will dispatch them.
|
|
396
|
-
Jira REST issues created through `tracker_create_issue` are assigned to that same owner by
|
|
397
|
-
default. Jira MCP creation forwards a concrete configured or caller-provided `assignee` to the
|
|
398
|
-
external MCP server.
|
|
399
|
-
Jira REST supports the same persistent workpad-comment flow as Linear through
|
|
400
|
-
`tracker_list_comments`, `tracker_comment`, and `tracker_update_comment`. Jira MCP maps those
|
|
401
|
-
neutral comment tools to `jira_get_comments`, `jira_add_comment`, and `jira_update_comment` by
|
|
402
|
-
default; override `trackers.<name>.mcp.tools.list_comments` or `update_comment` when the external
|
|
403
|
-
MCP server uses different tool names.
|
|
404
|
-
|
|
405
|
-
Direct Jira REST configuration:
|
|
406
|
-
|
|
407
|
-
```yaml
|
|
408
|
-
tracker:
|
|
409
|
-
kind: jira
|
|
410
|
-
trackers:
|
|
411
|
-
jira:
|
|
412
|
-
provider: jira
|
|
413
|
-
base_url: https://example.atlassian.net
|
|
414
|
-
email: $JIRA_EMAIL
|
|
415
|
-
api_key: $JIRA_API_KEY
|
|
416
|
-
project_keys: ["ENG"]
|
|
417
|
-
# Optional provider-native scope. When present, Lorenz combines it with active_states.
|
|
418
|
-
# jql: 'project = ENG AND labels in ("lorenz")'
|
|
419
|
-
```
|
|
420
|
-
|
|
421
|
-
Jira via an external MCP server:
|
|
422
|
-
|
|
423
|
-
```yaml
|
|
424
|
-
tracker:
|
|
425
|
-
kind: jira-mcp
|
|
426
|
-
trackers:
|
|
427
|
-
jira-mcp:
|
|
428
|
-
provider: jira-mcp
|
|
429
|
-
base_url: https://example.atlassian.net # optional; used for issue URLs when MCP payloads omit them
|
|
430
|
-
project_keys: ["ENG"]
|
|
431
|
-
mcp:
|
|
432
|
-
url: http://127.0.0.1:5123/mcp
|
|
433
|
-
token: $JIRA_MCP_TOKEN
|
|
434
|
-
tools:
|
|
435
|
-
search: atlassian_search_jira
|
|
436
|
-
read_issue: atlassian_get_jira_issue
|
|
437
|
-
update_status: atlassian_transition_jira_issue
|
|
438
|
-
list_comments: atlassian_get_jira_comments
|
|
439
|
-
comment: atlassian_add_jira_comment
|
|
440
|
-
update_comment: atlassian_update_jira_comment
|
|
441
|
-
create_issue: atlassian_create_jira_issue
|
|
442
|
-
```
|
|
443
|
-
|
|
444
|
-
### Local tracker (filesystem board)
|
|
445
|
-
|
|
446
|
-
The local tracker runs Lorenz against a directory of Markdown files, with no Linear API key or
|
|
447
|
-
workspace. See `WORKFLOW.local.md` for a complete example workflow.
|
|
448
|
-
|
|
449
|
-
Configure it with `kind: local` and a board `path` (default `.lorenz/local`):
|
|
450
|
-
|
|
451
|
-
```yaml
|
|
452
|
-
tracker:
|
|
453
|
-
kind: local
|
|
454
|
-
trackers:
|
|
455
|
-
local:
|
|
456
|
-
provider: local
|
|
457
|
-
path: .lorenz/local
|
|
458
|
-
id_prefix: "BOARD-" # optional, default "BOARD-"
|
|
459
|
-
active_states:
|
|
460
|
-
- Todo
|
|
461
|
-
- In Progress
|
|
462
|
-
terminal_states:
|
|
463
|
-
- Done
|
|
464
|
-
- Cancelled
|
|
465
|
-
```
|
|
466
|
-
|
|
467
|
-
Both `path` and `id_prefix` are local-specific and always defaulted, so a local workflow is valid
|
|
468
|
-
with just `kind: local`. `id_prefix` sets the issue-id prefix for the board: the tracker only treats
|
|
469
|
-
`<prefix><n>.md` files as issues and mints new ids with it, so one board can be `BOARD-1`, `BOARD-2`
|
|
470
|
-
and another `XXX-1`, `FEAT-1`, etc. It must be filesystem-safe (start alphanumeric, then only
|
|
471
|
-
letters, digits, `_` or `-`); an unsafe prefix is rejected at config load. Changing the prefix of an
|
|
472
|
-
existing board orphans files written under the old prefix (they stop matching), so set it up front.
|
|
473
|
-
|
|
474
|
-
Each issue is one file named `<prefix><n>.md` (for example `.lorenz/local/BOARD-7.md`, or
|
|
475
|
-
`.lorenz/local/XXX-7.md` with `id_prefix: "XXX-"`). The identifier is the file stem (`BOARD-7`).
|
|
476
|
-
The format is YAML front matter followed by a `# Title`
|
|
477
|
-
heading, the description, and an optional `## Comments` section:
|
|
478
|
-
|
|
479
|
-
<!-- prettier-ignore -->
|
|
480
|
-
```markdown
|
|
481
|
-
---
|
|
482
|
-
status: In Progress
|
|
483
|
-
labels:
|
|
484
|
-
- backend
|
|
485
|
-
---
|
|
486
|
-
|
|
487
|
-
# Fix the retry queue
|
|
488
|
-
|
|
489
|
-
The retry slot is not released when a worker fails.
|
|
490
|
-
|
|
491
|
-
<!-- lorenz:comments -->
|
|
492
|
-
## Comments
|
|
493
|
-
- 2026-05-29T12:00:00.000Z agent: Reproduced the leak; fix in progress.
|
|
494
|
-
```
|
|
495
|
-
|
|
496
|
-
- `status` (required) is the issue state. Active states (`Todo`, `In Progress`) mean the issue is
|
|
497
|
-
available to work; terminal states (`Done`, `Cancelled`) mean it is finished and must not be
|
|
498
|
-
reopened. Configure the exact sets with `active_states` / `terminal_states`.
|
|
499
|
-
- `labels` (optional) is a YAML list. Labels feed dispatch routing the same way Linear labels do.
|
|
500
|
-
- The `# Title` heading is the issue title; the text below it is the description.
|
|
501
|
-
- The `## Comments` section is managed by the `local_comment` tool. The hidden
|
|
502
|
-
`<!-- lorenz:comments -->` marker delimits it so a description that itself contains a
|
|
503
|
-
`## Comments` heading is never misparsed; treat the most recent comment block as the live
|
|
504
|
-
workpad.
|
|
505
|
-
|
|
506
|
-
Agent tools for `kind: local` (read and write, symmetric with `linear_graphql`):
|
|
507
|
-
|
|
508
|
-
- `local_update_status` - move an issue to a new status (args: `issueId`, `status`).
|
|
509
|
-
- `local_comment` - append a progress note to the issue's `## Comments` section (args: `issueId`,
|
|
510
|
-
`body`).
|
|
511
|
-
- `local_create_issue` - create a new board issue for out-of-scope follow-up work (args: `title`,
|
|
512
|
-
optional `body`, optional `status`).
|
|
513
|
-
- `local_read_issue` - read an issue's authoritative state: its current status, title, description,
|
|
514
|
-
and comments (args: `issueId`). Use it to re-read state and recover prior progress notes on a
|
|
515
|
-
continuation turn.
|
|
516
|
-
|
|
517
|
-
Concurrent writes (multiple agents or ensemble slots) to the same board file are serialized
|
|
518
|
-
in-process so a status change and comments are never lost. This assumes a single Lorenz daemon
|
|
519
|
-
owns the board; editing the `BOARD-<n>.md` files from another process at the same time is out of
|
|
520
|
-
scope.
|
|
521
|
-
|
|
522
|
-
To seed a board so you can try `kind: local` immediately, use the demo seeder, which writes
|
|
523
|
-
sample `BOARD-<n>.md` files through the same `BoardStore` the running tracker uses:
|
|
524
|
-
|
|
525
|
-
```sh
|
|
526
|
-
npx tsx sandbox/seed-local.ts # seeds ./.lorenz/local
|
|
527
|
-
npx tsx sandbox/seed-local.ts /tmp/demo-board # seeds an explicit directory
|
|
528
|
-
npx tsx sandbox/seed-local.ts .lorenz/local 2 # seeds only the first 2 issues
|
|
529
|
-
npx tsx sandbox/seed-local.ts /tmp/demo-board 3 XXX- # seeds XXX-1..XXX-3 (match trackers.local.id_prefix)
|
|
530
|
-
```
|
|
531
|
-
|
|
532
|
-
Point `trackers.local.path` at the directory you seeded and run Lorenz as usual. If you set a
|
|
533
|
-
custom `id_prefix`, pass the same prefix to the seeder so the seeded ids match what the tracker
|
|
534
|
-
expects.
|
|
535
|
-
|
|
536
|
-
### Slack tracker (mention + thread commands)
|
|
537
|
-
|
|
538
|
-
The Slack tracker treats an @-mention of a bot as an issue - in a channel message or in a thread
|
|
539
|
-
reply (a reply mention tracks its thread, anchored at the root, with the reply as the request).
|
|
540
|
-
The request's text is the issue title/description, threaded replies are comments, and the
|
|
541
|
-
issue's STATUS lives in the thread: the bot posts `status: <Name>` replies and humans transition
|
|
542
|
-
with `@bot !` command mentions; the latest event wins, and the bot mirrors the state onto its own
|
|
543
|
-
reaction for glanceability. See `WORKFLOW.slack.md` for a complete example workflow.
|
|
544
|
-
|
|
545
|
-
Set up a Slack app:
|
|
546
|
-
|
|
547
|
-
1. Create a Slack app at <https://api.slack.com/apps> (from scratch) in your workspace.
|
|
548
|
-
2. Under "OAuth & Permissions", add these **bot token scopes**:
|
|
549
|
-
- `channels:history` - read messages in public channels.
|
|
550
|
-
- `groups:history` - read messages in private channels (only if you watch private channels).
|
|
551
|
-
- `reactions:read` - read reactions (legacy status fallback and the tracking marker).
|
|
552
|
-
- `reactions:write` - mirror status onto the bot's own reaction and mark tracked threads.
|
|
553
|
-
- `chat:write` - post threaded replies (comments and `status:` transitions).
|
|
554
|
-
- `users:read` - resolve user ids to names for the `slack_user_info` tool (optional but
|
|
555
|
-
recommended).
|
|
556
|
-
|
|
557
|
-
Lorenz discovers issues by paging `conversations.history` and matching the bot's @-mention
|
|
558
|
-
in message text, so it does not need `app_mentions:read`. Only add that scope if you separately
|
|
559
|
-
wire up the Events API / `app_mention` subscription, which Lorenz does not use today.
|
|
560
|
-
|
|
561
|
-
`conversations.history` is rate-limited (newer non-Marketplace apps can be throttled to roughly
|
|
562
|
-
one request per minute), and each poll re-scans recent channel history. The shipped Slack
|
|
563
|
-
workflow therefore sets a conservative `polling.interval_ms` of `60000` (one minute), and you
|
|
564
|
-
should point it at dedicated, low-traffic channels so a busy channel does not trigger sustained
|
|
565
|
-
`429`s. The transport's `429`/`Retry-After` backoff and per-channel `poll_error` handling cover
|
|
566
|
-
transient limits on top of that.
|
|
567
|
-
|
|
568
|
-
3. Install the app to the workspace and copy the **Bot User OAuth Token** (starts with `xoxb-`).
|
|
569
|
-
Export it as `SLACK_BOT_TOKEN`; Lorenz resolves it into `trackers.slack.api_key`.
|
|
570
|
-
4. Find the app's **bot user id** (the `U...` id, shown on the app's "App Home" / via
|
|
571
|
-
`auth.test`). Export it as `SLACK_BOT_USER_ID` and reference it as
|
|
572
|
-
`trackers.slack.bot_user_id`.
|
|
573
|
-
5. Invite the bot to each channel you want it to watch (`/invite @your-bot`). A bot only sees
|
|
574
|
-
`*:history` for channels it has joined.
|
|
575
|
-
6. Collect the **channel IDs** (`C...`, from the channel's "About" panel) for those channels and
|
|
576
|
-
list them under `trackers.slack.channels`.
|
|
577
|
-
|
|
578
|
-
Configure it with `kind: slack`:
|
|
579
|
-
|
|
580
|
-
```yaml
|
|
581
|
-
tracker:
|
|
582
|
-
kind: slack
|
|
583
|
-
trackers:
|
|
584
|
-
slack:
|
|
585
|
-
provider: slack
|
|
586
|
-
channels:
|
|
587
|
-
- C0123456789
|
|
588
|
-
bot_user_id: $SLACK_BOT_USER_ID
|
|
589
|
-
emoji_states:
|
|
590
|
-
eyes: In Progress
|
|
591
|
-
white_check_mark: Done
|
|
592
|
-
x: Cancelled
|
|
593
|
-
active_states:
|
|
594
|
-
- Todo
|
|
595
|
-
- In Progress
|
|
596
|
-
terminal_states:
|
|
597
|
-
- Done
|
|
598
|
-
- Cancelled
|
|
599
|
-
```
|
|
600
|
-
|
|
601
|
-
`SLACK_BOT_TOKEN` (the bot token), a non-empty `channels` list, and `trackers.slack.bot_user_id`
|
|
602
|
-
(`SLACK_BOT_USER_ID`) are all **required**. The bot user id scopes issue creation to the bot's own
|
|
603
|
-
mentions: only messages that mention that exact user become issues, and only that leading mention
|
|
604
|
-
is stripped from the title. It is required so that ordinary human-to-human `<@U...>` mentions in a
|
|
605
|
-
watched channel never spawn agents or expose their text to workers. If it is unset or resolves
|
|
606
|
-
empty, config validation fails and the production transport fails closed (it scans nothing).
|
|
607
|
-
Channel entries resolve `$VAR` references the same way `bot_user_id` does.
|
|
608
|
-
`trackers.slack.assignee` is rejected for `kind: slack`: messages carry no assignee, so an
|
|
609
|
-
assignee-partitioned deployment would otherwise silently dispatch everything everywhere.
|
|
610
|
-
|
|
611
|
-
The issue identifier is the message reference in `<channel>:<ts>` form (for example
|
|
612
|
-
`C0123456789:1717000000.000100`); that is the `issueId` passed to the write tools. Issues also
|
|
613
|
-
carry a permalink (`{{ issue.url }}`, dashboard links) built from the workspace URL that
|
|
614
|
-
`auth.test` reports, and `slack_read_thread` returns the same permalink for linking the source
|
|
615
|
-
message from commits and PRs.
|
|
616
|
-
|
|
617
|
-
Status is derived from the issue's thread: the bot's own `status: <Name>` replies (posted by
|
|
618
|
-
`slack_update_status`) and human command mentions are ts-ordered events, and the latest wins.
|
|
619
|
-
The human commands are:
|
|
620
|
-
|
|
621
|
-
- `@bot !done` / `@bot !cancel` / `@bot !in progress` / `@bot !todo` - transition to the
|
|
622
|
-
standard state.
|
|
623
|
-
- `@bot !status <Name>` - transition to any configured active/terminal state (custom names
|
|
624
|
-
too).
|
|
625
|
-
- `@bot !reopen` - back to the first active state.
|
|
626
|
-
- Any other `@bot` mention on a terminal issue re-opens it: mentioning the bot again always
|
|
627
|
-
means "this needs attention".
|
|
628
|
-
|
|
629
|
-
Reactions are per-author in Slack (the bot cannot remove a human's reaction and vice versa), so
|
|
630
|
-
they are only the bot's visibility mirror, controlled by `emoji_states` (`:eyes:` ->
|
|
631
|
-
`In Progress`, `:white_check_mark:` -> `Done`, `:x:` -> `Cancelled` by default). Threads that
|
|
632
|
-
have never seen a status event fall back to the reaction-derived reading, so reaction-managed
|
|
633
|
-
threads keep working. Two optional keys tune tracking: `marker_emoji` (default `robot_face`) is
|
|
634
|
-
the reaction the bot drops on a reply-tracked thread's root, and `reply_lookback_days` (default
|
|
635
|
-
`2`) bounds how far back untracked threads are inspected for new reply-mention requests.
|
|
636
|
-
|
|
637
|
-
Agent tools for `kind: slack`, served by the `slack` tool pack (mounted by default alongside the
|
|
638
|
-
provider-neutral `tracker` pack):
|
|
639
|
-
|
|
640
|
-
- `slack_update_status` - set the issue's status by posting the bot's authoritative `status:`
|
|
641
|
-
thread reply, then mirror the bot's reaction (args: `issueId`, `status` - any configured
|
|
642
|
-
active/terminal state name).
|
|
643
|
-
- `slack_comment` - post a threaded reply on the source message as a comment (args: `issueId`,
|
|
644
|
-
`body`).
|
|
645
|
-
- `slack_read_thread` - read the issue's authoritative state: thread-derived status, source
|
|
646
|
-
message, request reply (for thread-tracked issues), reactions, permalink, and all replies
|
|
647
|
-
(args: `issueId`). Use it to re-read state, catch new human replies/commands, and recover
|
|
648
|
-
prior progress notes on a continuation turn.
|
|
649
|
-
- `slack_query` - read-only query over the tracked issues in the watched channels (bot-mention
|
|
650
|
-
roots plus bot-marked threads), with thread-derived state: filter with the shared JSON
|
|
651
|
-
predicate DSL, project fields, order, and page; `expand` adds `thread` and `reactions` (args:
|
|
652
|
-
`channels?`, `where?`, `select?`, `expand?`, `order_by?`, `limit?`, `offset?`).
|
|
653
|
-
- `slack_user_info` - resolve a `U...` user id to its profile: name, real name, display name,
|
|
654
|
-
bot flag (args: `userId`).
|
|
655
|
-
- `slack_channel_context` - read the channel conversation around a tracked issue's source
|
|
656
|
-
message, ascending (args: `issueId`, `before?` default 10 max 50, `after?` default 10 max 50).
|
|
657
|
-
|
|
658
|
-
There is no `slack_create_issue`, and the neutral `tracker_create_issue` reports itself as
|
|
659
|
-
unavailable on Slack: issues are created by humans @-mentioning the bot, not by the agent.
|
|
660
|
-
|
|
661
|
-
Routing note: Slack issues carry only hashtag-derived labels (a `#tag` in the message text
|
|
662
|
-
becomes the label `tag`); they are not otherwise routed or assigned. Dispatch treats a label as a
|
|
663
|
-
route only when it starts with `route_label_prefix`, so the Slack workflow sets
|
|
664
|
-
`route_label_prefix: route-`. Tag a message `#route-<name>` to route it: `#route-backend` becomes
|
|
665
|
-
the label `route-backend`, which dispatch resolves to the route `backend` (set `only_routes`
|
|
666
|
-
accordingly). Plain hashtags such as `#backend` stay non-route labels; with the default
|
|
667
|
-
`accept_unrouted: true` all Slack mentions are still picked up.
|
|
67
|
+
Linear is the default tracker: issues live in a Linear project, read access uses `LINEAR_API_KEY`,
|
|
68
|
+
and project selection uses `project_slug`. Route labels such as `Lorenz:backend` let multiple
|
|
69
|
+
instances share one project. Setup and configuration are in
|
|
70
|
+
[Linear tracker](./docs/trackers/linear.md). Other sources (Jira, Slack, local, memory) are covered
|
|
71
|
+
under [Trackers](./docs/trackers/index.md).
|
|
668
72
|
|
|
669
73
|
## Workflow Prompt
|
|
670
74
|
|
|
671
|
-
The prompt body
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
- `{{ issue.title }}`
|
|
675
|
-
- `{{ issue.description }}`
|
|
676
|
-
- `{{ issue.state }}`
|
|
677
|
-
- `{{ issue.state_type }}`
|
|
678
|
-
- `{{ issue.labels }}`
|
|
679
|
-
- `{{ issue.url }}`
|
|
680
|
-
- `{{ issue.id }}`
|
|
681
|
-
- `{{ issue.priority }}`
|
|
682
|
-
- `{{ issue.branch_name }}`
|
|
683
|
-
- `{{ issue.assignee_id }}`
|
|
684
|
-
- `{{ issue.created_at }}`
|
|
685
|
-
- `{{ issue.updated_at }}`
|
|
686
|
-
- `{{ issue.assigned_to_worker }}`
|
|
687
|
-
- `{{ issue.blocked_by }}`
|
|
688
|
-
- `{{ attempt }}`
|
|
689
|
-
- `{{ ensemble.enabled }}`
|
|
690
|
-
- `{{ ensemble.slot_index }}`
|
|
691
|
-
- `{{ ensemble.size }}`
|
|
692
|
-
|
|
693
|
-
Workspace tests render representative Liquid constructs: conditionals, null fallbacks, loops,
|
|
694
|
-
`forloop` metadata, nested blocker refs, and common filters.
|
|
75
|
+
The prompt body reads public issue and run fields as Liquid variables, such as
|
|
76
|
+
`{{ issue.identifier }}`, `{{ issue.title }}`, `{{ issue.description }}`, and `{{ attempt }}`. The
|
|
77
|
+
complete variable list is in the [Workflow prompt reference](./docs/reference/workflow-prompt.md).
|
|
695
78
|
|
|
696
79
|
## Skills
|
|
697
80
|
|
|
698
|
-
The `skills/` directory
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
- `lorenz-push` pushes branches and creates or updates PRs.
|
|
703
|
-
- `lorenz-pull` merges the latest `origin/main` into a working branch.
|
|
704
|
-
- `lorenz-land` monitors and merges approved PRs.
|
|
705
|
-
- `lorenz-debug` investigates stuck runs and execution failures.
|
|
81
|
+
The `skills/` directory holds orchestration skills (`lorenz-commit`, `lorenz-push`, `lorenz-pull`,
|
|
82
|
+
`lorenz-land`, `lorenz-debug`) referenced by the example workflows. Lorenz copies skills into
|
|
83
|
+
`.lorenz/skills/` in each prepared workspace before the agent starts. See
|
|
84
|
+
[Skills](./docs/agents/skills.md) for how `agent.skills` and tool-pack skills are resolved.
|
|
706
85
|
|
|
707
|
-
|
|
708
|
-
A `.gitignore` containing `*` is written alongside the copied skills so they are never committed.
|
|
709
|
-
Skills come from two places:
|
|
710
|
-
|
|
711
|
-
- **`agent.skills`** - a list of skill directories you maintain. Each entry is one skill directory
|
|
712
|
-
(e.g. `./skills/lorenz-land`) and is copied to `.lorenz/skills/<directory-name>`. Relative
|
|
713
|
-
paths resolve from the workflow file directory.
|
|
714
|
-
- **Tool packs** - a mounted tool pack can bundle the skill that documents it, so the skill ships
|
|
715
|
-
automatically when the tool is in use. The Linear pack bundles `lorenz-linear` (raw Linear
|
|
716
|
-
access via the injected `linear_graphql` tool for Codex or the `/mcp` endpoint for Claude), so
|
|
717
|
-
enabling Linear tools overlays that skill without listing it under `agent.skills`.
|
|
86
|
+
## Observability
|
|
718
87
|
|
|
719
|
-
|
|
720
|
-
|
|
88
|
+
The terminal dashboard shows agents, throughput, runtime, token usage, rate limits, sessions, the
|
|
89
|
+
retry queue, and dispatch blocks. The web dashboard exposes the same runtime snapshot over a local
|
|
90
|
+
HTTP server, started with `--port` or `server.port`. Routes, the WebSocket stream, and `/mcp` tool
|
|
91
|
+
serving are documented in [Observability](./docs/observability.md).
|
|
721
92
|
|
|
722
|
-
##
|
|
93
|
+
## Contributing
|
|
723
94
|
|
|
724
|
-
|
|
725
|
-
sessions, retry queue, and dispatch blocks. The web dashboard exposes the same runtime snapshot
|
|
726
|
-
through a local HTTP server.
|
|
95
|
+
### Requirements
|
|
727
96
|
|
|
728
|
-
|
|
97
|
+
[mise](https://mise.jdx.dev/) manages Node 24 and pnpm 9 from `mise.toml`:
|
|
729
98
|
|
|
730
99
|
```sh
|
|
731
|
-
|
|
100
|
+
mise trust
|
|
101
|
+
mise install
|
|
102
|
+
pnpm install
|
|
732
103
|
```
|
|
733
104
|
|
|
734
|
-
|
|
105
|
+
### Run
|
|
735
106
|
|
|
736
|
-
|
|
737
|
-
- `/api/v1/state`
|
|
738
|
-
- `/api/v1/runs`
|
|
739
|
-
- `/api/v1/runs?id=<run-id>`
|
|
740
|
-
- `/api/v1/refresh`
|
|
741
|
-
- `/api/v1/:issue_identifier`
|
|
107
|
+
Build, then run the CLI against a workflow file:
|
|
742
108
|
|
|
743
|
-
|
|
109
|
+
```sh
|
|
110
|
+
pnpm build
|
|
111
|
+
pnpm start -- WORKFLOW.md
|
|
112
|
+
pnpm start:once -- --dry-run --no-tui WORKFLOW.md
|
|
113
|
+
```
|
|
744
114
|
|
|
745
|
-
|
|
746
|
-
observability server. The server also starts automatically for Claude workflows so the ACP bridge
|
|
747
|
-
can reach those tools.
|
|
115
|
+
### Workspace Layout
|
|
748
116
|
|
|
749
|
-
`
|
|
750
|
-
|
|
117
|
+
`apps/cli` is the composition root; `packages/*` is the provider-agnostic engine;
|
|
118
|
+
`extensions/*` are the tracker backends; `test/` holds workspace-level tests. The layering rules
|
|
119
|
+
and the recipe for adding a tracker live in [Architecture](./docs/architecture.md) and the
|
|
120
|
+
[Source map](./docs/source-map.md).
|
|
751
121
|
|
|
752
|
-
|
|
122
|
+
### Testing
|
|
753
123
|
|
|
754
124
|
```sh
|
|
755
125
|
mise run tidy
|
|
@@ -757,52 +127,25 @@ mise run check
|
|
|
757
127
|
```
|
|
758
128
|
|
|
759
129
|
`mise run tidy` formats and applies lint fixes. `mise run check` runs typecheck, build, tests, and
|
|
760
|
-
lint.
|
|
130
|
+
lint. When running Vitest directly, rebuild first so tests exercise the current compiled packages.
|
|
761
131
|
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
```sh
|
|
765
|
-
pnpm typecheck
|
|
766
|
-
pnpm build
|
|
767
|
-
pnpm lint
|
|
768
|
-
pnpm test
|
|
769
|
-
pnpm test:watch
|
|
770
|
-
```
|
|
132
|
+
### Live Tests
|
|
771
133
|
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
## Live Tests
|
|
775
|
-
|
|
776
|
-
Live tests are opt-in and launch real CLIs or services in isolated workspaces.
|
|
134
|
+
Live tests are opt-in and launch real CLIs or services in isolated workspaces:
|
|
777
135
|
|
|
778
136
|
```sh
|
|
779
137
|
pnpm test:live:codex
|
|
780
138
|
pnpm test:live:linear-codex
|
|
781
139
|
pnpm test:live:claude
|
|
782
140
|
pnpm test:live:ssh
|
|
783
|
-
pnpm test:live:linear-sandbox
|
|
784
141
|
```
|
|
785
142
|
|
|
786
|
-
`
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
- `LORENZ_TS_CODEX_ACP_COMMAND` overrides the Codex ACP bridge command for live tests.
|
|
791
|
-
- `LORENZ_TS_CLAUDE_ACP_BRIDGE_COMMAND` enables Claude live tests.
|
|
792
|
-
- `LORENZ_TS_CLAUDE_ACP_BRIDGE_ARGS` supplies Claude ACP bridge args as a JSON string array.
|
|
793
|
-
- `LINEAR_API_KEY` is required for Linear live tests and MCP canaries.
|
|
794
|
-
- `LINEAR_PROJECT_SLUG` selects the Linear project for `pnpm test:live:linear-codex`.
|
|
795
|
-
- `LORENZ_LIVE_SSH_WORKER_HOSTS` is a comma-separated list of real SSH workers.
|
|
796
|
-
- When `LORENZ_LIVE_SSH_WORKER_HOSTS` is unset, the SSH live test can use disposable local
|
|
797
|
-
workers if Docker, `ssh-keygen`, and Codex auth are available.
|
|
798
|
-
- `LORENZ_LIVE_DOCKER_CODEX_AUTH_JSON` points disposable workers at a Codex auth file. The
|
|
799
|
-
default is `~/.codex/auth.json`.
|
|
800
|
-
- `CLAUDE_CODE_OAUTH_TOKEN` or `LORENZ_LIVE_DOCKER_CLAUDE_CODE_OAUTH_TOKEN` lets disposable
|
|
801
|
-
workers run the remote Claude canary.
|
|
802
|
-
- `LORENZ_TS_REQUIRE_REMOTE_CLAUDE=1` makes the remote Claude canary mandatory in the SSH live
|
|
803
|
-
test.
|
|
143
|
+
`LORENZ_LIVE_SSH_WORKER_HOSTS` is a comma-separated list of real SSH workers. When it is unset, the
|
|
144
|
+
SSH live test can use disposable local workers if Docker, `ssh-keygen`, and Codex auth are
|
|
145
|
+
available. `LINEAR_API_KEY` is required for Linear live tests, and
|
|
146
|
+
`LORENZ_TS_CLAUDE_ACP_BRIDGE_COMMAND` enables the Claude live tests.
|
|
804
147
|
|
|
805
|
-
|
|
148
|
+
### Packaging
|
|
806
149
|
|
|
807
150
|
```sh
|
|
808
151
|
pnpm build
|
|
@@ -812,18 +155,12 @@ pnpm --filter @lorenz/cli pack --dry-run
|
|
|
812
155
|
The CLI package includes the built binary. Workspace documentation, workflow fixtures, and test
|
|
813
156
|
evidence stay at the workspace root.
|
|
814
157
|
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
The checked-in workflow files are executable fixtures:
|
|
818
|
-
|
|
819
|
-
- `WORKFLOW.md`
|
|
820
|
-
- `WORKFLOW_FULL_ACCESS.md`
|
|
158
|
+
### Compatibility Contracts
|
|
821
159
|
|
|
160
|
+
The checked-in workflow files (`WORKFLOW.md`, `WORKFLOW_FULL_ACCESS.md`) are executable fixtures.
|
|
822
161
|
`pnpm test` guards workflow docs, prompt rendering, dashboard snapshots, runtime behavior, and CLI
|
|
823
162
|
documentation. Update the fixture and the matching test together when the public contract changes.
|
|
824
163
|
|
|
825
164
|
## License
|
|
826
165
|
|
|
827
|
-
See [CHANGELOG.md](CHANGELOG.md) for notable
|
|
828
|
-
|
|
829
|
-
This project is licensed under the [Apache License 2.0](LICENSE).
|
|
166
|
+
See [CHANGELOG.md](CHANGELOG.md) for notable changes. This project is licensed under the [Apache License 2.0](LICENSE).
|