Remote execution
Point one of a flow's environments at another machine with -e, and the work the flow does there (the files an agent edits, the builds and tests it runs) happens on that machine. Use this when the build, the data or the GPUs are on a host you reach over ssh, and your coding agent and its sign-in are on the machine in front of you. This page takes you from ssh build-box working to a flow building on it, and shows each of the places the agent's CLI itself can run. The part of Humanize that does this is called CogAnchor (hmz.coganchor).
- the agent's CLI, installed and signed in
- its account and its link to the model provider
- its sessions, which is what a trace is read from
- the project the agent reads and writes, at the host's own paths
- every command it runs: builds, tests,
git - the network those commands reach
That split is what you get with the host saved with its harness here, local, which is what a host with no CLI of the agent's own needs. By default the CLI installed on the host runs over there instead: see Where the agent runs.
Try it
With ssh build-box working and the project at /home/me/build/myproject on it, save the host with its harness here, in ~/.hmz/settings.yaml (or at /settings runtimes, as below):
runtimes:
ssh:
build-box:
host: build-box
harness: localThen run a flow that has a role for another machine, such as onbox:
hmz exec -f onbox \
-a builder=claude/claude-haiku-4-5-20251001:low \
-a reviewer=claude/claude-haiku-4-5-20251001:low \
-e box=ssh@build-box/home/me/build/myproject \
-p budget.duration=10m "Make test_calc.py pass."The builder's tools name the host's paths, and what it changed is on the host when the run ends:
● builder is working
● Read(/home/me/build/myproject/test_calc.py)
● Read(/home/me/build/myproject/calc.py)
● Edit(/home/me/build/myproject/calc.py)
● Done! The bug was in `calc.py:2` — the `add` function was using subtraction … the test now passes.
✻ Worked for 16s · builder
● reviewer is workingBefore you start
- Humanize and one coding agent here, installed and signed in: see Installation.
ssh build-boxworks from here without a password prompt. Humanize uses your own ssh config, agent and keys, and adds nothing.ssh -o BatchMode=yes build-box trueis the test: it must exit 0.- On the host: Linux or macOS on any architecture, Python 3.12 or newer, and the project already at the path you will name. You install nothing else there: Humanize brings what it needs.
- This machine is Linux on x86-64 or aarch64.
- A flow with a role for another machine. The flows Humanize ships and the official flowverse lists all work in the directory you start in, so none of them takes an
-e. This page usesonbox, a two-role flow you can save into your project.
How remote execution works
A flow declares environment roles: named places its agents work. One is always the workspace, the directory you started hmz in, and Humanize fills it. Any other is yours to name, with -e role=… on the command line or on its row at /flow. onbox has one, box: its builder works there, and its reviewer reads in the workspace.
What -e takes after the = is up to three parts, backend@provider/workdir. The @provider is a machine you saved under a name, or, for ssh, any host in brackets; leave it off for this machine:
-e box= | Where the role's work happens |
|---|---|
ssh@build-box/home/me/build/myproject | that directory on the host you saved as build-box |
ssh@[build-box]/home/me/build/myproject | that directory on a host nobody saved, as ssh reaches it: host, user@host, host:port, or an alias from your ssh config |
ssh@build-box/~/build/myproject | under the home directory of whoever ssh logs in as |
ssh@build-box | the workdir the host saved as build-box was saved with |
docker@gpubox/home/me/myproject | a container of its own on the docker daemon saved as gpubox, holding that directory of the daemon's host |
docker/home/me/myproject | the same, on docker's default here, with nothing saved |
apple-container/Users/me/myproject | an Apple container of its own on this Mac, holding that directory |
slurm@hpc/lustre/me/myproject | a job of its own on the Slurm cluster saved as hpc, on whichever node the cluster gives it, holding that directory of the shared filesystem |
local/srv/project | a directory on this machine |
Every turn the flow gives box works on the host: the files it reads and writes are the host's, every command it runs runs there, and the flow's own steps on box, such as onbox's git diff, run there too.
The agent's CLI, and whatever supervises it, is its harness. Where the harness runs decides whose sign-in the agent uses, where its sessions are kept, and which network its model is reached over. Nothing on the command line says where: a host you saved under a name says it for every run on it, as its harness setting, one place and nowhere else.
harness | The agent's CLI runs |
|---|---|
env (the default) | on the host itself, the CLI installed there |
local | here, reaching the host's files and commands |
ssh:<name>, docker:<name>, swarm:<name>, apple-container:<name>, slurm:<name> | on another saved runtime, reaching the host's work from there |
A host nobody saved, such as ssh@[build-box], has env. Where the setting cannot hold the harness (no CLI on the host, say) the run is refused rather than put anywhere else. Work in the workspace always has its harness here. The places are worked through one by one below.
Example: build on a host, review here
This is the run from Try it, in full. build-box has the project and Python, and no coding agent CLI, and is saved with its harness local.
hmz exec -f onbox ①
-a builder=claude/claude-haiku-4-5-20251001:low
-a reviewer=claude/claude-haiku-4-5-20251001:low
-e box=ssh@build-box/home/me/build/myproject ②
-p budget.duration=10m ③
"Make test_calc.py pass."● builder is working
● Bash(find /home/me/build/myproject -type f -name "*.py" | head -20) ④
● Read(/home/me/build/myproject/test_calc.py)
● Read(/home/me/build/myproject/calc.py)
● Edit(/home/me/build/myproject/calc.py)
● Bash(cd /home/me/build/myproject && python test_calc.py) ⑤
● Done! The bug was in `calc.py:2` — the `add` function was using subtraction … the test now passes.
✻ input 58 · output 1.1k · cache_read 143.4k · cache_write 8.6k · claude-haiku-4-5-20251001 · builder
✻ Worked for 16s · builder
● reviewer is working ⑥
● The original code had a bug: the `add()` function was performing subtraction (`a - b`) instead
of addition (`a + b`). The diff correctly fixes this by changing the operator from `-` to `+`.
✻ Worked for 5s · reviewer
…What each part means
-f onboxnames a flow with an environment role. A flow without one refuses-e:ralph_loop has no environment role 'box'; available roles are none.-e box=ssh@build-box/home/me/build/myprojectputs theboxrole on the host saved asbuild-box, in that directory. In brackets,ssh@[build-box]would be whateverssh build-boxreaches with nothing saved, so with the CLI on the host rather than here. The path is the host's, and has to exist there.-p budget.duration=10mis the budget, as for any flow. A remote run spends like any other.- The builder's tools name
/home/me/build/myproject, the host's path. The agent sees the host's files at the host's own paths, so a path in a compiler error or a test failure is one you can open on the host. Bash(… python test_calc.py)ran onbuild-box, with the host's Python and the host's network. Nothing the agent runs runs on this machine.- The reviewer works in the workspace, here, and reads a
git diffthe flow ran on the host. One flow can mix roles here and there.
build-box is saved with its harness local, so the builder's CLI ran here. At the prompt the transcript says so as the role's first turn there starts:
❯ Make test_calc.py pass.
builder's harness runs here
── builder
● builder is workingCheck that it worked
The change is on the host:
$ ssh build-box "cd /home/me/build/myproject && git diff"
diff --git a/calc.py b/calc.py
…
- return a - b
+ return a + b/epicslists the run, and a trace of it shows the builder's sessions like any other, each with where its harness went.- While a run goes, the monitor's page for
boxsays the same on itsharnessrow:local: on this machine; what it runs lands here. See the monitor's environment page. /home/me/build/myprojectalso exists here: it is the copy the agent read and wrote through, kept at the same path. See the path is taken here too.
Example: set it up at the prompt
The same run, set up in hmz rather than on a command line, with the host saved once so that nothing about it has to be typed again.
1. Save the host. /settings runtimes opens the Runtimes page. Choose Add a runtime…, then ssh host. Type the host, fill in only what your ssh config does not already say, and tab to Done. The line under the list says what pressing it will do:
hmz › /settings › Runtimes › Add an ssh host ● unsaved changes
A machine where flow environments run. Connects using your ssh config plus
settings configured here. Keys are specified by path and never read.
╭──────────────────────────────────────────────────────────────────────────╮
│ host build-box │ ①
│ hostname, IP address, or user@host:port │
│──────────────────────────────────────────────────────────────────────────│
│ name build-box │ ②
│ name used in -e and /flow │
│──────────────────────────────────────────────────────────────────────────│
│ user — │
│ username; leave blank to use your ssh config │
│──────────────────────────────────────────────────────────────────────────│
│ port — │
│ leave blank to use your ssh config, or 22 │
│──────────────────────────────────────────────────────────────────────────│
│ identity file — │
│ path to private key │
│──────────────────────────────────────────────────────────────────────────│
│ proxy jump — │
│ jump host to connect through, if any │
│──────────────────────────────────────────────────────────────────────────│
│ options — │
│ additional ssh options: KEYWORD=VALUE, … │
│──────────────────────────────────────────────────────────────────────────│
│ workdir /home/me/build/myproject │ ③
│ default working directory when -e specifies none: /abs or ~/path │
│──────────────────────────────────────────────────────────────────────────│
│ falls back to — │
│ runtimes to try in order if this one cannot: docker:box, ssh:gpu2 │
│──────────────────────────────────────────────────────────────────────────│
│ harness runs on local │ ④
│ where an agent's harness runs, and nowhere else: env (the CLI on this │
│ runtime's machine), local (here), or ssh:<name>, docker:<name>, │
│ swarm:<name>, apple-container:<name>, slurm:<name>; blank for env │
╰──────────────────────────────────────────────────────────────────────────╯
adds ssh/build-box, and checks its resources
Done
enter done ←/→ move tab list esc backDone saves it and reaches it at once, as a run would:
╭──────────────────────────────────────────────────────────────────────────╮
│ ssh │
│ build-box build-box · working directory: │
│ /home/me/build/myproject │
╰──────────────────────────────────────────────────────────────────────────╯
ssh/build-box answers: home /root; 64 CPUs, 2015G ⑤2. Put the role on it. /flow @local/onbox opens the flow's setup. Its environment role is a row under its agents; enter on box opens a form of the parts -e takes after box=, on the host row. enter there lists the saved ssh hosts: choose build-box. Its saved workdir comes with it, and while you leave that as it is, the spec leaves it out. Nothing is left to answer, so the focus moves on to Done:
hmz › @local/onbox › Environment for box ● unsaved changes
The machine and working directory for this environment role. Choosing a
machine saved on the runtimes page of /settings by name includes its saved
working directory.
╭──────────────────────────────────────────────────────────────────────────╮
│ backend ssh ▾ │
│ a machine reached over ssh │
│──────────────────────────────────────────────────────────────────────────│
│ host build-box ▸ │
│ build-box · working directory: /home/me/build/myproject │
│──────────────────────────────────────────────────────────────────────────│
│ workdir /home/me/build/myproject │
│ leave blank to use saved default: /home/me/build/myproject │
│──────────────────────────────────────────────────────────────────────────│
│ as -e ssh@build-box │
│ full -e spec: typing one sets the rows above │
╰──────────────────────────────────────────────────────────────────────────╯
sets box to ssh@build-box when the flow is saved
Done
enter done ←/→ move tab list esc back3. Save the flow, and type the task.
hmz › @local/onbox ● unsaved changes
Configure each role: an agent (CLI, account, model and effort) or an
environment; then what the flow takes and what an epic may spend.
╭──────────────────────────────────────────────────────────────────────────╮
│ builder claude/claude-haiku-4-5-20251001:high ▸ │
│ agent │
│──────────────────────────────────────────────────────────────────────────│
│ reviewer claude/claude-haiku-4-5-20251001:high ▸ │
│ agent │
│──────────────────────────────────────────────────────────────────────────│
│ box ssh@build-box ▸ │ ⑥
│ environment │
│──────────────────────────────────────────────────────────────────────────│
│ budget set ▸ │
│ what an epic may spend: stops at 10m │
│──────────────────────────────────────────────────────────────────────────│
│ profile flow ○ off ▾ │
│ traced only │
│──────────────────────────────────────────────────────────────────────────│
│ profile environment ○ off ▾ │
│ traced only │
╰──────────────────────────────────────────────────────────────────────────╯
Save
enter open tab actions esc closeWhat each part means
hostis anythingsshtakes: an alias, a name, an address,user@host:port. Typed asuser@host:port, it is split into theuserandportrows for you.nameis what-eand/flowcall it from now on:-e box=ssh@build-box.workdiris where a role put on this host works when nothing more is said, sossh@build-boxalone meansssh@build-box/home/me/build/myproject.harness runs onis the host's harness setting: where the CLI of an agent working on it runs, below.localkeeps it here, which a host with no CLI needs; blank is the default,env, the CLI on the host.answersis the check: Humanize reached the host with nobody there to type a password, and read its home, CPUs and memory. A host that cannot be reached says why on this line.- The
boxrow holds what the form set, as-espells it:ssh@build-box, the saved host and its saved workdir.
Your answers are kept with the flow's setup in this directory, like its agents. Import ~/.ssh/config on the same page lists every Host your config names, each switched on unless its row says why not (already imported, say), with the focus on Done: enter saves them in one go, each still pointing at its Host so the config stays the one place it is written.
Save a host under a name
A host that needs more than a name (a login, a port, a key, a jump host) is worth saving once, as in step 1 above. enter on one saved host opens its menu: check reaches it again, edit reopens its form, and the Remove button under them forgets it. Saved hosts are offered by name on the host row of every environment form at /flow. What each field means is in Machines › Runtimes.
A docker daemon is saved on the same page with Add a runtime… and docker host, and named with -e box=docker@<name>/…: see Containers. Its endpoint may be a saved ssh host, the daemon on that host, reached with everything the host says. A docker swarm is saved there too, with docker swarm under Add a runtime… -- the same form for one of its managers, with where its tasks may go in place of GPU ids -- and named with -e box=swarm@<name>/…, or swarm/… for the swarm this machine manages. A Slurm cluster is saved there too, with slurm cluster: see A Slurm cluster.
A Slurm cluster
Where the machines are a Slurm cluster and this machine can sbatch to it, let the cluster choose the node: -e box=slurm[@<name>]/<workdir> gives the role a job of its own, one task on one node, submitted when the run starts and cancelled when it ends. The job asks for the CPUs, memory and GPUs the flow's role declares, and the cluster keeps it to them; everything the agent runs lands on the job's node, inside that allocation. The agent's CLI runs where the runtime's harness setting says (Where the agent runs): by default the CLI on the node, which the nodes then need; set it to local to run it here, supervised, so that the nodes need no CLI.
hmz exec -f onbox \
-a builder=claude/claude-haiku-4-5-20251001:low \
-a reviewer=claude/claude-haiku-4-5-20251001:low \
-e box=slurm@hpc/lustre/me/myproject \
-p budget.duration=30m "Make test_calc.py pass."slurm/…, naming no runtime, submits with the cluster's defaults. To say the partition, the account and the rest, open /settings, Runtimes, choose Add a runtime…, then slurm cluster:
| Row | What to put |
|---|---|
partition | where the jobs go; blank for the cluster's default. The name follows it until you type one |
account, qos, time, constraint | what sbatch is told as --account, --qos, --time (4:00:00) and --constraint; each blank for the cluster's own |
python | a Python 3.12 or newer the nodes can run, where theirs is older (see below) |
via | srun or tcp: how the node is reached once the job runs (see below) |
max jobs, cpus, memory | what this runtime's jobs may hold at once, all told, across every run on this machine; blank for no limit |
max wait | seconds a job may wait in the queue for a node; past it the role falls back, or the run stops saying why Slurm kept it waiting |
workdir | the project's path when -e names none |
Saving it asks sinfo which nodes of the partition may take a job, and says so.
What to know before the first run:
- The workdir must be on a shared filesystem, at the same path here and on every node a job may land on: nothing is copied.
~/…is your home. - The nodes need Python 3.12 or newer. A cluster's nodes often have only an old system Python (3.8 on the cluster this was tested on). Put one on the shared filesystem, for example with
uv python install 3.12, and name it inpython:~/.local/share/uv/python/cpython-3.12.11-linux-x86_64-gnu/bin/python3.12. via: srunworks on any cluster, but holds ports. Each connection is ansrun --overlapstep of the job, and each livesrunholds listening ports on this machine from the cluster'sSrunPortRange(scontrol show config | grep SrunPortRange). A cluster may allow only a few dozen:SrunPortRange=30927-30951caps it at about 8 connections at once, which a flow with several roles and agents runs out of.via: tcpholds none. The job itself runs Humanize's serving half on its node, on a free port, and each connection presents a secret only this run knows;srunis used just to put Humanize on the node and read the port. It needs this machine to reach the nodes' ports, and its traffic, authenticated but not encrypted, stays within the cluster's network.- A job waiting in the queue keeps no one else waiting. Every role's environment is brought up at once, and other runs on this machine can submit while yours waits.
Details: Machines › Slurm environments.
Where the agent runs
The harness is the agent's CLI and whatever supervises it. Where it runs is a setting of the runtime the work is on, not of the run: the harness runs on row of a saved host's form, its harness. It names one place, and the harness goes there or the run is refused; nothing else is tried:
│ harness runs on docker:gpubox │
│ where an agent's harness runs, and nowhere else: env (the CLI on this │
│ runtime's machine), local (here), or ssh:<name>, docker:<name>, │
│ swarm:<name>, apple-container:<name>, slurm:<name>; blank for env │env (blank) | local | ssh:<name>, docker:<name> | |
|---|---|---|---|
| The CLI runs | on the host | here | on that runtime |
| Signed in as | the host's CLI, or the @account sent there | this machine's CLI, or the @account | that machine's CLI |
| Sessions kept | on the host | here, with the run | on that machine |
| Needs on the host | Python 3.12, the CLI, and Landlock for a fenced role | Python 3.12 | Python 3.12 |
| Refused when | the CLI is not on the host, it cannot hold the role's fence, or a hook decides whether each tool runs | never | it cannot be reached, has no share left, or the role is fenced |
Each role is settled once per machine, as its first turn there starts, and before the flow is called every role is settled against every machine of the run: where the setting cannot hold the harness, the run is refused before anything runs, saying why. The setting read is the one of the runtime the work is on; a runtime a harness is sent to is used as it is, its own setting never read.
A host given in brackets and never saved, and docker's default here (docker/…), have env: the CLI must be on the host, or in the image. To have it here instead, save the host and set its harness to local.
local: here
The CLI runs here, with this machine's sign-in and its sessions kept with the run. It works in a copy of the host's directory that Humanize keeps here, and every command it runs is sent to the host. The transcript says builder's harness runs here. Use it when the host has no CLI, when it is one you would not give your account to, or when its CLI is signed in as somebody else. A role with a hook that decides whether each tool runs, such as the builder of humanize1:rlcr, needs it: the CLI's hooks name a program on this machine.
The path is taken here too
The copy here has the host's path
The copy the agent works in is kept at the same path on this machine. That path has to be one you can create here, and must be free: absent, empty, or Humanize's copy of that same host from an earlier run.
- A directory with other files in it is refused rather than overwritten, so
ssh@build-box/home/me/code/myprojectfails when your own checkout is at/home/me/code/myprojecton this machine. - A path you may not create here fails the turn, as
cannot keep the local copy of the work at /home/me/build/myproject: Permission denied: /home/me, marked(unmirrored: …).
A docker environment keeps its copy under ~/.hmz/envs/mirrors/ instead, so its workdir may be your own checkout.
env: on the host
The CLI installed on the host runs there, in the host's directory, with nothing copied. The transcript says builder's harness runs on its environment's machine. Use it when the host is where the CLI should live: a machine with its own sign-in, or a network only it reaches.
- Its sign-in is the host's. An agent with no
@accountruns as the host's CLI is signed in. One with an@accounthas the account's variables and credential files sent to the host for each turn, and taken away after. An account signed in with a login that renews itself (Codex with ChatGPT, Claude Code with a subscription, and other OAuth logins) is sent only while no other turn is using it, and what the host renewed it to is brought back: two copies renewing apart get the login revoked. A turn that finds it in use the other way is refused with… signs in with a token that refreshes itself …; give such roles an account signed in with a key, or set the host's harness tolocal(more). - Never copy your own sign-in to the host. Sign the CLI in there itself. A copy of
~/.codex/auth.jsonor~/.claude/.credentials.jsonrenews apart from yours and signs this machine out. - Its sessions are kept on the host, not with the run here.
- The flow's own tools are not offered to it, and a role with a hook that decides whether each tool runs is refused:
a pre_tool_use hook cannot gate the tools of a CLI on another machine; set the harness of the runtime it is on to local. A hook that answers the agent's questions is not one of those: a question comes back from wherever the CLI runs. - A role not granted everything needs Landlock on the host to hold its permission.
Where the host has no CLI, or cannot hold the role's permission, the run is refused before the flow starts, with exit status 2, saying what to do about it:
hmz exec: error: ssh@build-box: claude's harness cannot run where its harness setting (env) says: claude is not installed on ssh@build-box: npm i -g @anthropic-ai/claude-code there, or set the harness of the runtime it is on to localInstalled is on the PATH an ssh command gets
A CLI your shell profile puts on the PATH, such as one under ~/.local/bin, is not found there. ssh build-box 'command -v claude' prints nothing in that case: install it where that command finds it, such as /usr/local/bin.
ssh:<name>, docker:<name>: on another runtime
The CLI runs on another saved runtime, and reaches the host's work from there. Use it when neither this machine nor the host should hold the agent: a machine that is the only one signed in, say, or a docker daemon with containers to spare. The runtime is opened as an environment of its own for the run, probed with the others and taken down with them:
harness | The CLI runs |
|---|---|
ssh:gpu-box | on the host saved as gpu-box, in its saved workdir, else the login's home |
docker:gpubox | in a container of its own on the daemon saved as gpubox, holding its saved workdir; a daemon on this machine saved without one holds an empty $TMPDIR/humanize-<uid>/harness |
The run is refused when it cannot be reached or opened, when a daemon has no share left for the container (its max containers reached, say), and for any role not granted everything. Its other caveats:
- Only a role granted everything. A permission cannot be held around a CLI on another machine, so a fenced role is refused:
a fence cannot hold a harness that runs on another machine. Most flows' roles run at the default grant; see Permissions. - The CLI must be installed and signed in there. Nothing checks before the turn: a machine without it fails the turn with
claude: not found on PATH. No@accountis sent there. - It opens a port here, on every interface, for as long as the run lasts, for the two machines to meet. Set
HUMANIZE_RENDEZVOUS_PORTto pin it for a firewall. - It cannot be the host itself: a harness naming its own runtime is refused when it is saved; that is
env.
What it needs
| Where | What it needs |
|---|---|
| This machine | Linux on x86-64 or aarch64, and the agent's CLI installed and signed in, unless the harness runs elsewhere |
| The host | Linux or macOS on any architecture, Python 3.12 or newer, ssh access, and the project already at that path. The CLI too, for env, the default. |
| The flow | a role for the host, besides its workspace |
A Mac gives a remote command no python3 on its PATH, so Humanize also looks where Homebrew and the python.org installer put one, and says what it looked for if it finds none.
Variations
- Several roles, several hosts. Give each environment role its own
-e, or several in one separated by commas:-e box=ssh@build-box,gpu=ssh@gpu-box/~/train. - A path under the login's home.
ssh@build-box/~/build/myprojectis the same directory for whoever ssh logs in as, without spelling out the home. - A container instead of a host.
-e box=docker/home/me/myprojectgives the role a container of its own: see Containers. - A job on a Slurm cluster.
-e box=slurm@hpc/lustre/me/myprojectgives the role a job of its own on whichever node the cluster gives it: see A Slurm cluster. - A directory on this machine.
-e box=local/srv/projectputs the role somewhere other than the workspace, on this machine. - Another host when this one is down. Fill in
falls back toon a saved host's form (ssh:build-2, docker:box): where-e box=ssh@build-box/…cannot reachbuild-box, the role goes to the first of those that it can, and the run says so —hmz exec: ssh:build-box cannot hold 'box': …; using ssh:build-2. Where the agent's CLI runs is thenssh:build-2's harness setting. See Machines › Falling back.
If it goes wrong
Most of these stop hmz exec before anything runs, as hmz exec: error: …, with exit status 2.
| You see | What to do |
|---|---|
onbox needs an environment for 'box'; specify each with -e ROLE=BACKEND[@PROVIDER]/WORKDIR | Say where box is, with -e or at /flow. |
-e 'box=ssh@build-box/…': no ssh host is saved as 'build-box'; write box=ssh@[build-box]/… for a host not saved | Save it under that name, or put it in brackets. |
ralph_loop has no environment role 'box'; available roles are none | That flow only works in the directory you start it in. |
onbox: 'workspace' is the workspace the run started in and cannot be set with -e | Start hmz in that directory instead. |
there is no ssh host build-box: … | Nothing resolves the name. Check your ssh config. |
could not reach build-box over ssh: … | Run ssh build-box yourself and fix what it says. |
could not reach build-box over ssh: … no python 3.12 or newer on this machine; … | The host has no Python 3.12 or newer that Humanize can find. Install one there. |
the workdir /home/me/build/myproject is not there | Put the project on the host at that path. |
… 'box' needs 8 GPUs, and the environment given has 0 | The flow asks more of the host than it has. Pick another host. |
… already contains files and is not a Humanize mirror … | The same path here holds other files. See the path is taken here too. |
… mirrors ssh://old-box, not ssh://build-box … | That path here holds Humanize's copy of another host. Use another path. |
cannot keep the local copy of the work at … on the first turn | The path cannot be created here. Use one you can create, or set the host's harness to env. |
… claude's harness cannot run where its harness setting (env) says: claude is not installed on ssh@build-box: … | env (the default, and a host nobody saved) and no CLI on the host's PATH. Install it there, or save the host with its harness local. |
… harness cannot run where its harness setting (env) says: a pre_tool_use hook cannot gate the tools of a CLI on another machine; … | The flow gates the role's tools with a hook. Save the host with its harness local. |
… harness cannot run where its harness setting (docker:gpubox) says: … a fence cannot hold a harness that runs on another machine | A harness runtime for a role not granted everything. Set the harness to local or env. |
build-box: 'somewhere' is not where a harness runs: env, local or <ssh|docker|swarm|apple-container|slurm>:<runtime name> | Saving the host refused its harness. Spell it as one of those. |
More, with what causes each, are in Troubleshooting.
Good to know
- Network. With the harness here, the agent's own connection to its model provider stays here, and the commands it runs use the host's network. With
envboth are the host's. - Timing. What a command changes on the host is visible to the agent once that command has exited. See What is not guaranteed.
- Other ways to reach a machine. A port left listening, a running container, or a target reached by hand are below the flow API: see the Remote execution reference.
The flow used on this page
onbox: build on the box, review here
Save it as .hmz/flows/onbox/__init__.py in your project, and it is offered as @local/onbox. What each line means is the Writing Flows guide's to explain.
"""Build on the box, review here."""
from hmz.flows import (
Agent,
AgentCollection,
Env,
EnvCollection,
FlowContext,
FlowParams,
LocalEnv,
ShellEnvMixin,
flow,
)
class Box(Env, ShellEnvMixin): ...
class Agents(AgentCollection):
builder: Agent
reviewer: Agent
class Envs(EnvCollection):
workspace: LocalEnv # the directory the run starts in
box: Box # wherever -e says
@flow(agents=Agents, envs=Envs, params=FlowParams)
async def onbox(task: str, *, agents: Agents, envs: Envs, params: FlowParams, ctx: FlowContext):
builder, reviewer = agents["builder"], agents["reviewer"]
working = await builder.spawn()
await builder.run(task, session=working, env=envs["box"])
for _ in range(3):
_, diff, _ = await envs["box"].exec(["git", "diff"])
reading = await reviewer.spawn()
review = await reviewer.run(
f"Say what is wrong with this diff:\n\n{diff}", session=reading, env=envs["workspace"]
)
await builder.run(review, session=working, env=envs["box"])Next steps
- Containers: a container for a role, reached the same way
- Permissions: what a role may touch, on the host as here
- Remote execution reference: the arrangements behind a harness setting, and where the account lives in each
- Machines › Runtimes: every field of a saved host, its harness among them
- Humanize in CI: the same
-ein a scheduled job