The map of what is built: packages, processes, storage, Workers, and gaps, and the path of one change through the code.
usine is one long-running Node process, the orchestrator, that polls the GitHub issues and pull requests of the repositories that an operator registers. When an issue or pull request matches a Loop, the orchestrator opens a Run and has a coding agent work on it. Loops and Agents are Markdown files in the operator’s instance folder; the engine has no built-in Loop. The same process serves a web UI on 127.0.0.1, where the operator approves and steers the work and follows its progress.
Each try of a Run is an Attempt. The Attempt’s Pi parent runs inside the orchestrator as a pi-durable task and delegates the coding to Claude Code, Codex, or Pi children. The children run on a Worker: the orchestrator’s own machine, or a Linux machine the orchestrator connects to over SSH. An SSH Worker can be on a private network behind a Gateway VM, which adds the GitHub token to the Worker’s requests so that no token is on the Worker. CONTEXT.md defines the vocabulary, and docs/adr/ explains the design decisions.
The system
Section titled “The system”Inside the orchestrator process
Section titled “Inside the orchestrator process”flowchart TB
browser["Browser<br/>apps/web"]
github["GitHub"]
subgraph process["Orchestrator process: usine up"]
api["Operator API"]
triggers["PollingTriggers<br/>ScheduleTriggers"]
engine["RunEngine<br/>a fiber per open Run"]
runtimeDb[(".usine/<br/>runtime.sqlite")]
scheduler["StepExecutor<br/>Worker slots"]
executor["Executor<br/>local or SSH"]
attempt["AttemptRuntime<br/>Pi parent task"]
durableDb[(".usine/<br/>durable.sqlite")]
children["Child backends<br/>Claude Code, Codex, Pi"]
end
workers["Workers"]
browser -- "HTTP :7331" --> api
triggers -- "polls, label writes" --> github
api --> engine
triggers -- "submit" --> engine
engine --- runtimeDb
engine --> scheduler --> executor --> attempt --> children
attempt --- durableDb
children --> workers
The orchestrator serves /api/* and the built web assets on 127.0.0.1:7331 (cli.ts:10, http.ts:243). Inside the process, the operator API is in packages/orchestrator/src/operator, the polling triggers are in packages/orchestrator/src/polling, the schedule triggers in schedule-triggers.ts, and the Worker scheduler in scheduler.ts. The same process creates the GitHub App installation tokens that the polls and the Attempts use (github-app.ts:185).
Workers, the Gateway, and GitHub
Section titled “Workers, the Gateway, and GitHub”flowchart TB orchestrator["Orchestrator<br/>Executor and child backends"] local["Local Worker<br/>.usine/workers/"] sshDirect["SSH Worker<br/>token file per Attempt"] sshGateway["SSH Worker<br/>behind a Gateway"] gateway["Gateway VM<br/>:80 git and llm<br/>:3128 forward proxy<br/>:7070 admin"] github["GitHub<br/>github.com, api.github.com,<br/>uploads.github.com"] orchestrator -- "local processes" --> local orchestrator -- "SSH" --> sshDirect orchestrator -- "SSH through<br/>the Gateway" --> sshGateway sshGateway -- "private network" --> gateway local -- "Attempt token" --> github sshDirect -- "Attempt token" --> github gateway -- "adds the token" --> github
One executor handles every Worker of an instance: the SSH executor when every Worker in usine.yaml is ssh, the local executor otherwise (worker-engine.ts:13). With a Worker of another kind, startup fails with UnsupportedWorkerError (scheduler.ts:76), so usine up can’t use the exe.dev executor in @usine/executor. An SSH Worker without a Gateway receives a token file per Attempt (credentials.ts:51). For a Worker behind a Gateway, the executor sends the caller token with SendEnv (commands.ts:101). The SSH hop through the Gateway comes from a ProxyJump entry in the operator’s ~/.ssh/config, because the executor passes only the port and identity file to ssh (ssh.ts:22).
The second diagram doesn’t show three paths. The executor calls the Gateway’s admin API on 127.0.0.1:7070 through an SSH local forward that the operator keeps open (admin.ts:79). The Gateway’s llm.<domain> host forwards to Anthropic or OpenAI when the Gateway has a credential for that provider (proxy.ts:206). The forward proxy on port 3128 opens tunnels to package hosts and to the hosts of the harness logins (egress-proxy.ts:241). The Gateway’s listeners are in environment.ts:121, and Workers and the Gateway describes each path.
The run host of ADR 0006 no longer exists. #176 deleted the @usine/run-host package and its byte protocol (ADR 0006, amendment of 2026-10-11), so no usine package is installed on a Worker. The Attempt channel envelopes are still the transcript format.
Packages and apps
Section titled “Packages and apps”The last column lists workspace dependencies only. effect is a peer dependency of every published library and a direct dependency of apps/server and apps/web.
| Package | Responsibility | Depends on |
|---|---|---|
@usine/core 0.3.0, packages/core |
Domain model: Event, Run and its state machine, Step, Attempt, failures, salvage, the Attempt channel envelopes, Worker and repository identities. | none |
@usine/expression 0.2.0, packages/expression |
Checks and evaluates CEL expressions with @marcbachmann/cel-js 8.0.0. |
none |
@usine/config 0.2.1, packages/config |
Parses and validates usine.yaml, Loop files, and Agent files, and loads an instance folder with per-file quarantine. Contains a YAML writer that no production code calls. |
core, expression |
@usine/github 0.1.2, packages/github |
GitHub App authentication and token minting, change detection, GraphQL payload slices, label writes. | core |
@usine/engine 0.3.0, packages/engine |
Event ledger, the claim of a Run for each Event, supersede and revoke, one fiber per open Run, cycle budgets, Attempt admission and drain, the SQLite store. | core |
@usine/agents 0.3.0, packages/agents |
The Claude Code, Codex, and Pi child backends, where children run (ChildHost), prompt assembly, harness preflight. |
core |
@usine/durable 0.0.0, packages/durable |
Runs each Attempt’s Pi parent as a pi-durable task in the orchestrator, forwards its activity as envelopes, resumes it after a restart. | agents, core |
@usine/executor 0.2.1, packages/executor |
The Worker contract for local, SSH, and exe.dev Workers: preflight, checkout, credentials, launch, cancel, interventions, salvage, harvest, recovery. | agents, core, durable, gateway |
@usine/gateway 0.0.0, packages/gateway |
The Gateway service: reverse proxy for GitHub and LLM providers, forward proxy with an allowlist, admin API for attachments. Binary usine-gateway. |
core |
@usine/orchestrator 0.2.1, packages/orchestrator |
The composition: polling and schedule triggers, Worker scheduling, the operator service and its HTTP contract, the Board projection, the runtime layer. | config, core, durable, engine, executor, expression, github |
@usine/server 0.2.1, apps/server |
The usine CLI (up, backup, export-run); serves the operator API and the web build. |
config, core, durable, engine, expression, github, orchestrator |
@usine/web, apps/web, private |
The React web UI: projects, Boards, the Run drawer, Loop pause controls. | core, orchestrator |
The public subpaths are the exports of each package’s package.json:
| Package | Public subpaths |
|---|---|
@usine/core |
/agent, /agent/brain, /agent/capability, /agent/session, /attempt, /attempt/channel, /attempt/failure, /attempt/outcome, /attempt/salvage, /cycle-budget, /event, /instance, /item, /loop, /repository, /run, /run/state, /step, /step/harvest, /step/result, /worker |
@usine/expression |
/expression-engine, /marcbachmann-cel |
@usine/config |
/agent, /board, /factory, /file, /instance, /instance-folder, /label, /loop, /parser, /payload-slices, /selector, /trigger, /yaml/writer |
@usine/github |
/github-app, /item-writer, /label-writer, /local-secrets, /polling |
@usine/engine |
/cursor, /cycle-budget, /database, /engine, /item-projection, /local-sqlite, /loop-control, /repository-label-projection, /run-reader, /step-executor, /transcript, /trigger-condition |
@usine/agents |
/child-host, /children, /claude, /codex, /pi, /pi/authentication, /preflight, /prompt, /runtime |
@usine/durable |
/attempt-runtime, /backup, /harness, /live, /testing |
@usine/executor |
., /local, /ssh, /exe-dev |
@usine/gateway |
., /admin, /node |
@usine/orchestrator |
/board/projection, /credentials, /exe-dev, /idle-triggers, /label-reconciler, /local, /operator, /operator/http, /orchestrator, /polling-triggers, /runtime, /schedule-triggers, /scheduler, /ssh |
@usine/server |
none, binary usine |
@usine/web |
none |
packages/run-host doesn’t exist at this commit.
The graph below shows the same dependencies, with an arrow from each package to the packages it imports. It omits an arrow when a longer path already implies it: @usine/orchestrator also depends on core directly, for example.
flowchart TB server["server"] --> orchestrator["orchestrator"] web["web"] --> orchestrator orchestrator --> config["config"] orchestrator --> engine["engine"] orchestrator --> executor["executor"] orchestrator --> github["github"] config --> expression["expression"] config --> core["core"] executor --> durable["durable"] executor --> gateway["gateway"] durable --> agents["agents"] engine --> core github --> core gateway --> core agents --> core
How one change moves through the tree
Section titled “How one change moves through the tree”sequenceDiagram participant T as Triggers participant E as RunEngine participant S as StepExecutor participant X as Executor participant D as AttemptRuntime T->>E: submit Note over E: record the Event,<br/>open or queue a Run E->>E: launch fiber E->>S: reserve slot E->>S: execute S->>X: prepare, launch Note over X: checkout<br/>on the Worker X->>D: execute Note over D: Pi parent<br/>delegates D-->>E: envelopes D-->>X: parent report Note over X: harvest X-->>E: StepResult Note over E: finishAttempt
-
usine up(up.ts:104) loads the instance folder and the GitHub App credentials, then buildsruntimeLayer(runtime.ts:37). Without GitHub App credentials, it serves the setup page and doesn’t build the runtime layer. -
PollingTriggersandScheduleTriggersturn GitHub state and clock ticks intoSubmissions and callRunEngine.submit(engine.ts:52). -
The engine records the Event and opens or queues a Run in one transaction (
recordAndClaim, store.ts:103), then starts the Run’s fiber (RunDriver.launch, run-driver.ts:131), which runsdrive(run-driver.ts:341). -
The fiber reserves a Worker slot from
StepExecutor(scheduler.ts:131), begins the Attempt, and callsStepExecutor.execute(scheduler.ts:163), which callsExecutor.prepare, thenlaunch. -
The executor prepares a checkout on the Worker. Its durable session (durable-session.ts:120) passes the Attempt to
AttemptRuntime.execute(durable-session.ts:145). -
The Pi parent delegates to children with its
subagenttool (subagent.ts:31). The engine appends their activity, as envelopes, to the transcript. -
When the parent finishes, the executor harvests commits and pull requests from the checkout (harvest.ts:135), and the engine closes the Attempt and the Run (
finishAttempt, store.ts:362).