Skip to content

Files ​

Every file and directory humanize reads or writes: under its home, under a workspace's .hmz/, and elsewhere on this machine and on the machines environments run on.

Roots ​

RootPathNotes
home (H below)$HUMANIZE_HOME, else ~/.hmzNot created in advance; the first writer creates it. The provider stores create every missing level with mode 0700; other writers leave it at the umask. A ~/.humanize is moved here.
user flows~/.hmz/flowsAlways the literal ~; does not follow HUMANIZE_HOME.
workspace<workspace>/.hmz/<workspace> is the directory hmz runs in. Not added to .gitignore. A <workspace>/.humanize/ is moved here.
cache~/.cache/humanize/Does not follow HUMANIZE_HOME.
machine (M below)<tmp>/humanize-<uid>/Python's tempfile.gettempdir() ($TMPDIR, else /tmp) and the user's id: what is this machine's alone, which a home directory several machines share must not hold: caches, scratch copies, sockets. Made 0700 by the first look at it; refused if anyone else can write it. A cleaner of temporary files may empty it; everything in it is made again when next needed. See Temporary.
a remote machine's home${HUMANIZE_HOME:-$HOME/.hmz} in the login shell thereHolds envs/ for ssh environments. A $HOME/.humanize there is moved here.

Moved from .humanize ​

These directories were called .humanize before. Each old one is renamed to its new name the first time humanize looks for it, with no message:

OldNewWhen
~/.humanize~/.hmzfirst look at the home in a process; not when HUMANIZE_HOME is set
<workspace>/.humanize<workspace>/.hmzfirst look at the workspace's flows, first flow forked into them, or first bundle written there; not when it is ~/.humanize (moved as the home is) or the directory HUMANIZE_HOME names
$HOME/.humanize on an ssh machine$HOME/.hmzwhen the machine is probed; not when HUMANIZE_HOME is set there

The rename happens only while the new directory does not exist. When both exist, the new one is used and the old one is left as it is; nothing is merged. A rename that fails (a parent that cannot be written, a mount point) is not an error: the old directory is just not used. Snapshots and rewinds leave a .humanize/ at the top of the git worktree alone, as they leave a .hmz/ there.

With HUMANIZE_HOME set, ~/.humanize is not moved, so flows of your own still in ~/.humanize/flows are not found until you move them to ~/.hmz/flows.

<ws> below is a workspace's absolute resolved path with every character outside [A-Za-z0-9] replaced by - (/home/you/code → -home-you-code; distinct paths can collide, e.g. /a/b.c and /a/b-c). Timestamps written into files are UTC, %Y-%m-%dT%H:%M:%S.mmmZ, unless stated.

Layout ​

H/
├── settings.yaml                       everything /settings sets: workspaces, machine settings, fallbacks, added CLIs, runtimes
├── .settings.yaml.lock                 held by each writer of settings.yaml
├── history.jsonl                       lines typed at the TUI prompt
├── providers/<cli>/<name>/             accounts
│   ├── provider.json
│   └── home/ user/ config/             credential files the CLI writes
├── flowverses/<name>/                  one per flowverse, official included
│   ├── index/                          the index clone
│   └── installed/[<user>/]<flow>/      installed flows, each with its .installed.json
├── envs/
│   ├── <workdir-name>-<digest>/{clones,scratch,worktrees}/
│   └── mirrors/<container, service or job>/<digest>/
└── epics/<ws>/<stamp>-<hex6>/          one run

M/
├── prices.json                         the price table
├── releases.json                       the newest release of hmz
├── models/<cli>/<account>.json         model catalogues, `_local` for the CLI's own sign-in
├── harness/                            workdir of a harness an affinity puts on a docker daemon or Apple containers here
├── pinned/<blake2b-8(url)>/<sha>/      checkouts of git+ refs and of releases installed
├── skills/<owner>-<repo>-<sha256[:12]>/  skill repositories a never-installed flow names by URL
├── .<backend>.<name>.lock              held while a run allocates from a runtime
├── .pi.models.lock                     held while a turn adds a gateway to pi's models.json
├── compiled/{pi,qwen}/                 Node compile caches
├── docker-ssh/<sha256[:16]>/ssh        ssh shim for docker over ssh
└── patched/<cli>-<pid>-<rand>/         patched CLI copies (unused in production)

~/.hmz/flows/                      your flows (flowverse `user`)
<workspace>/.hmz/
└── flows/                              this project's flows (flowverse `local`)

Settings and state ​

H/settings.yaml ​

Settings is the schema. Written through hmz.coganchor.settings: by hmz.runtime.settings.Settings, and by the fallbacks, added CLIs and runtimes.

PropertyValue
FormatYAML (yaml.safe_dump, key order kept); read with yaml.safe_load
Writeunder the lock: re-read, this writer's one change made to it (rules), written to .settings.yaml.<random>.new (mode 0600 for a new file, the existing file's otherwise), fsynced, renamed over
Lockflock(LOCK_EX) on H/.settings.yaml.lock (0600, opened read-only, kept) for each write, waited for up to 30 s and then gone without; released by the kernel if the writer dies. Readers take no lock.
Unreadable, missing, not a mappingread as empty; never an error
Write failure, or unreadable at a writenot written over; ignored by Settings, OSError for a fallback, added CLI or runtime
Removednever; forget removes one workspace's entry

H/history.jsonl ​

PropertyValue
Writerthe TUI, on every submitted prompt line
Line{"at": "%Y-%m-%dT%H:%M:%S.%fZ", "workdir": "<abs path>", "text": "<line>"}
Rulesappended; blank lines and a repeat of the previous line are not written; no size limit
Readat TUI start: this workdir's lines, or every line if it has none
Safe to deleteyes

M/prices.json ​

json
{"source": "https://openllmprices.com/data/prices.json", "etag": "…", "fetched": 1790000000.0,
 "date": "…", "models": {"<id>": {"provider": "…", "name": "…",
   "per_million": {"input": 3.0, "output": 15.0, "cache_read": 0.3, "cache_write": 3.75}}}}

Refreshed when older than 24 h, with a conditional GET (a 304 only touches the mtime), from HUMANIZE_PRICES, at most one attempt an hour per process, 20 s timeout: by the TUI as it opens, in the background; and by every run as it starts (hmz exec, the SDK, the TUI's), in the background unless the run has a finite cost limit, which waits for it before its first turn. Written to .prices.json.<random>.new, fsynced and renamed. Each fetch first deletes any .prices.json.*.new or prices.json.* (the older naming) more than 10 min old, left by a process that exited mid-write.

M/releases.json ​

json
{"latest": "0.3.0"}

The newest release of hmz PyPI lists, from HUMANIZE_RELEASES, 10 s timeout. Asked again by the TUI and hmz web as they open, in the background, when older than 24 h, and by every hmz update. Written whole and renamed. Safe to delete.

Model catalogues ​

A cache, so on this machine rather than in H: $TMPDIR/humanize-<uid>/models/<cli>/<name>.json for an account, and $TMPDIR/humanize-<uid>/models/<cli>/_local.json for the CLI's own sign-in (no account name starts with _). An account's is deleted when the account is removed:

json
{"asked": "2026-09-30T05:33:55Z", "models": [{"name": "…", "efforts": ["low", "high"], "swarms": false}]}

Asked again after 7 days. Written to .<file>.<random>.new, fsynced and renamed; a new file gets the umask's mode, an existing one keeps its own.

H/providers/<cli>/<name>/ ​

An account. <name> matches [A-Za-z0-9][A-Za-z0-9._-]*. Directories 0700.

provider.json (0600 from creation, written to .provider.json.<random>.new, fsynced and renamed):

FieldType
clistrthe CLI (the directory name wins on read)
namestrthe account name (the directory name wins)
waystrthe sign-in way; default env
env{str: str}variables every turn under it runs with (Environment)
args[str]extra CLI arguments
madestr%Y-%m-%dT%H:%M:%SZ

A fallback key written by an older version is ignored, and dropped the next time the account is written.

home/, user/, config/ hold the credential files the CLI itself writes when it signs in, redirected from where it would write them at home:

CLIFiles
claudehome/.credentials.json, home/.claude.json, user/.claude.json, config/anthropic
codexhome/auth.json
agyhome/antigravity-oauth-token
grokhome/auth.json, home/mcp_credentials.json
kimihome/credentials, home/oauth
pihome/auth.json, home/auth.json.lock
opencode, mimohome/auth.json, home/mcp-auth.json
cursor-agenthome/cli-config.json, config/cursor/auth.json, user/.cursor/auth.json
mcodehome/config.yaml, home/auth
dsh, omp, qwennone

Removing an account deletes its directory.

H/local/<cli>.json ​

Written by older versions to hold the CLI's own sign-in's account fallback. No longer read; safe to delete.

Flows ​

H/flowverses/<name>/ ​

One flowverse: its index clone and the flows installed from it, side by side. Deleted whole, in one rename, by remove (not official). A directory here without index/ is still listed, as a flowverse with nowhere to fetch from.

H/flowverses/<name>/index/ ​

A git clone --depth 1 of the flowverse's index: flows/[<user>/]<flow>/<version>/flow.yaml. Cloned into .index.XXXXXXXX beside it and renamed into place; a leftover .index.* older than 60 s is removed before the next clone. Fetch is git fetch --depth 1 origin HEAD then git reset --hard FETCH_HEAD. The origin URL is read from .git/config. Read as YAML, never imported.

H/flowverses/<name>/installed/[<user>/]<flow>/ ​

A flow installed from that flowverse's index -- under <user>/ for one it lists under a user, which is removed with that user's last flow: the release's subdir at its commit, without .git and __pycache__ (a single <flow>.py as __init__.py), every skill its roles name by URL fetched into its skills/<name>/, and .installed.json. Written into .<flow>.XXXXXXXX beside it and renamed into place, the release it replaces moved aside into that directory first and deleted with it; a leftover .<flow>.* older than 600 s is removed before the next install of that name. Replaced by an update, deleted by uninstall and by removing the flowverse. Imported where flows are listed and run.

.installed.json, written with the copy before the rename, JSON (indented 2):

KeyValue
verse, owner, namethe flowverse, the user ("" for a flow listed bare) and the flow; must match the directories, or it is not read as installed
version, committhe release, and the commit it was copied from
repo, ref, subdiras the manifest said
dependencies{flow: range}, as the manifest said; checked by later installs and uninstalls
skills{url: [skill, …]}: each URL its roles name, to the skills install fetched into its skills/ for it

~/.hmz/flows/ and <workspace>/.hmz/flows/ ​

Flowverses user and local: <name>/__init__.py or <name>.py (Where flows live). humanize writes here only when a flow is copied here (to .<name>.*, then renamed). Never deleted by humanize.

Environments ​

<state>/envs/ ​

On the machine an environment is on; <state> is H here and ${HUMANIZE_HOME:-$HOME/.hmz} over ssh.

PathIsLifetime
envs/<name≤32>-<blake2b(workdir)>/everything derived from one workdirkept
…/clones/<id≤40>-<blake2b(id)>/a temporary copy (cp -a --reflink=auto)removed when its call ends, or kept for a resumable run
…/clones/<…>.lockflock(LOCK_EX|LOCK_NB) held by the process holding the copy (this machine only)unlinked on destroy
…/clones/<…>.part.<pid>/, ….part.gone….<pid>/a copy being made; one being removedtransient
…/scratch/<id>-<blake2b(id)>/a scratch directoryas copies
…/worktrees/<ref|head>-<hex8>/a derive_worktree with no dirnever removed
envs/mirrors/<container>/<digest>/the local mirror of a docker or apple-container environment's workdirremoved with the container
envs/mirrors/<service>/<digest>/the local mirror of a swarm environment's workdirremoved with the service
envs/mirrors/<job name>/<digest>/the local mirror of a slurm environment's workdirremoved with the job

Names are deterministic, so a resumed run finds the same copy. envs/ may be deleted while no run uses it. write on a local environment goes through .<hex12>.hmz-tmp beside the file, then rename.

In your repository. snapshot writes refs refs/hmz/snapshots/<name> (commits authored humanize <humanize@localhost>) using a temporary index <index>.hmz-snapshot.<pid>. Refs are never removed by humanize.

Containers. A docker environment's container is named humanize-<provider>-<role>-<hex8> and labelled humanize=<uid>, humanize.provider, humanize.role, humanize.host, humanize.pid, and humanize.cpus, humanize.memory, humanize.gpus where set. Only the workdir is bind-mounted. A swarm environment's service is named and labelled the same way, on the swarm its runtime's manager manages, and an apple-container environment's container likewise, less humanize.gpus, by Apple's container on this Mac. A slurm environment's job is named the same way and carries the same labels, with humanize.name, URL-encoded in its comment; nothing is mounted, its workdir being the same path on a shared filesystem.

M/harness/ ​

An empty directory mounted as the workdir of a harness an affinity puts on a docker runtime whose daemon is on this machine, or on an Apple container, saved without a workdir (Harness placement): a container needs a directory of this user's on the host to mount. Created by the run; nothing is written into it by humanize.

Runs ​

H/epics/<ws>/<stamp>-<hex6>/ ​

One run (Tracing › Epics has every schema). <stamp> is %Y%m%dT%H%M%S.mmmZ (UTC), <hex6> random. A directory without epic.jsonl is not listed.

FileWrittenFormat
epic.jsonlappended per event, open/write/close under a thread lock; no fsyncevents
epic.<flow>_<hex6>.jsonlone per flow callrecords
resume.jsonlresumable runs only; compacted via .resume.jsonl.<random>.new + fsync + rename, then appended O_APPENDjournal
profile.jsonlprofiled runs onlyprofile
host.logruns held by a host process only; appended (0600), never rotatedthat process's descriptors 1 and 2 while the run is the one it holds: output of the CLIs the run started, and the carrier's failures
.heldempty, 0600; flocked exclusively by the process running the run until ended is writtena run whose .held is locked is still going, and is not picked up
sessions/<cli>/…by the CLI itself, redirectedthe CLI's own layout
traces/*.trace.jsonon demand; plain writeChrome trace

Epics are never deleted by humanize.

Caches ​

PathIs
M/compiled/pi/, M/compiled/qwen/NODE_COMPILE_CACHE for pi and Qwen Code; written by Node
M/docker-ssh/<sha256[:16]>/sshshim for docker over ssh:// with options; directory and file 0700; touched on each use, written again if gone
M/patched/<cli>-<pid>-<rand>/0700; directories of dead processes are removed
~/.cache/humanize/shadows/<sha256(path)[:16]>.json{"shadow": "<abs path>", "target": "<target>"} per mirror; moved by HUMANIZE_SHADOWS; never removed

Every path in this section is safe to delete while humanize is not running.

Temporary ​

PathIsRemoved
$TMPDIR/hmz-fence-XXXXXXXX/ (0700)a fenced process's TMPDIR; cache/<var> inside for redirected cacheswhen the process ends (left on SIGKILL)
the same path, on a machine a supervised agent's commands run onthat agent's commands' TMPDIR therekept
$TMPDIR/humanize-hook-*/hook.sock, humanize-tools-*/tools.sock, humanize-preload-*/said.socksockets a CLI reports hooks, tool calls and preload events onwith the session
$TMPDIR/hmz-dsh-*/humanize.patch.yml, hmz-qwen-*/per-session CLI configurationwith the session
$TMPDIR/humanize-<uid>/pinned/<blake2b-8(url)>/<sha>/checkouts of git+ refs, and of the releases installed: a full clone with --no-checkout into .<uuid>, checked out detached at <sha>, renamed into place; one per commitnever; cloned again when missing
$TMPDIR/humanize-<uid>/ (0700, refused if anyone else can write it): humanize-<digest>.pyz (0700), <stamp>.digest (0600)the humanize bundle copied to other machines, one per source tree it was built from, and which tree built whichany humanize-* or *.digest in it untouched for 14 days, when another bundle is built; a run touches the one it uses at least hourly
$TMPDIR/humanize-<uid>/skills/<owner>-<repo>-<sha256(url)[:12]>/clones of skill repositories a role of a flow that was never installed names by URL (Skills), fetched again each run that names themkept
$TMPDIR/humanize-<uid>/daemon.sock, daemon.json (0600)the daemon of this machine and user, which every workspace's runs are reached through: its socket, and {"pid": int, "started": "%Y-%m-%dT%H:%M:%SZ", "kind": "daemon", "protocol": int} via .daemon.json.<random>.new (mkstemp), fsync and renamewhen the daemon closes
$TMPDIR/humanize-<uid>/daemon.lock (0600)flock(LOCK_EX|LOCK_NB) for the daemon's life; released by the kernel on exit. Deleting it under a running daemon allows a second daemonkept
$TMPDIR/humanize-<uid>/daemon.log (0600)what belongs to no run: the daemon's stdout and stderr, and a host process's before it holds a run; what belongs to a run is the epic's host.logkept, never rotated
$TMPDIR/humanize-<uid>/models/<cli>/<name>.json, _local.jsonmodel catalogues of each account and of the CLI's own sign-inan account's when it is removed; the rest kept
$TMPDIR/humanize-*a docker or Apple container environment's cid file and machine shadow, or a SlurmConfig machine's shadowwith the container or job
$TMPDIR/humanize-<uid>/.<backend>.<name>.lockempty; held with flock(LOCK_EX) while containers of a docker or Apple container runtime are sized and started, or services of a swarm counted, created and waited for, or jobs of a Slurm runtime counted and submitted (not while they wait in the queue), so two runs on this machine never allocate from one runtime at oncekept
${XDG_RUNTIME_DIR:-$TMPDIR}/humanize-ssh-<uid>/%C[-<hex8>] (0700)ssh control sockets (HUMANIZE_SSH_REUSE)120 s after last use
/dev/shm/hmz-<pid>-<hex16>-*/<n>.<file> (0700/0600)credential copies staged for a turn (≤ 1 MiB each)on close; dead-pid directories swept

On other machines ​

PathIs
$HOME/.cache/humanize/humanize-<digest>.pyzthe humanize bundle on an ssh machine or a Slurm job's node (/tmp/humanize/… in a container); checked against <digest> on arrival and when already there, written to f.<pid> and moved into place; every run line touches the one it runs; other humanize-*.pyz* untouched for 14 days are removed by the next install
$HOME/.cache/humanize-mirrors/<sha256[:16]>/ on an ssh machine; /tmp/humanize-mirrors/<sha256[:16]>/ in a containera remote harness's mirror of the workspace
${HUMANIZE_HOME:-$HOME/.hmz}/envs/as above
/tmp/humanize-slurm-<job>.out on a Slurm job's nodewhat a via: tcp job's serving half says on its way up, which its port is read from; left to the node
a mktemp -d directory (umask 077)per-session files of a native turn, including projected credentials (0600); removed after the turn
.humanize-carried/<uuid>claim file inside a directory carried to the target

Read, never written ​

PathRead for
~/.ssh/config and its Includesimporting ssh hosts
each CLI's home (Backend homes)sessions to trace; credentials of @local; tally
$DSH_HOME (~/.dsh): settings.yaml, .credentials.yaml, .envDeepSeek Harness key and endpoint

Antigravity's permission profiles hmz-read-only.md, hmz-read-only-web.md and hmz-offline.md are written into Antigravity's own home, ~/.gemini/antigravity-cli/agents/, when their content changes.

Retention ​

Nothing prunes epics/, worktrees under envs/, snapshot refs, M/compiled/, M/docker-ssh/ or history.jsonl. Delete them by hand; an epic's sessions/ is the only copy of that run's conversations. Bundles, here and on other machines, go once nothing has used them for 14 days, and so do the $TMPDIR/humanize-<uid>.pyz and .stamp an earlier humanize shared between checkouts.

Released under the Apache-2.0 licence.