arizuko

arizuko › concepts › the ant

ant

An ant is the agent you talk to: one Claude Code process in its own KVM guest. Each group has one ant, and the folder of the group on disk is the ant. The diary, the skills and PERSONA.md are files in that folder.

The folder is the agent

Every group lives at /srv/data/arizuko_<instance>/groups/<folder>/ on the host. When a message arrives, runed starts a guest and mounts that folder at /home/node/ inside the guest. The agent sees what is in the folder:

groups/solo/inbox/
  PERSONA.md       persona — name, tone, voice
  CLAUDE.md        the operator's runbook for this group
  work.md          what the agent is doing right now
  diary/           dated work log, survives restarts
  episodes/        compacted day, week and month summaries
  facts/           durable knowledge
  users/           one file per person the agent talks to
  .claude/         CLAUDE.md, skills/, session state

If you copy the folder, you copy the ant. If you delete the folder, the ant is gone. No separate database row holds the persona.

Memory is layers of files in the folder

Memory is also files in the folder, in layers. The work file and the sender's file go into every prompt. The diary and the episodes have levels: a file per day, per week and per month, named by its period, in one flat folder per lens. When a task starts, the newest file of each level goes into the prompt, marked as folder-wide, because the diary and the episodes cover the whole folder and not one thread. The facts stay on disk until the agent searches for them:

layer        file in the group home           reaches the turn
──────────── ──────────────────────────────── ─────────────────────────────
now          ~/work.md                        pushed whole, every turn
diary        ~/diary/YYYYMMDD.md,             newest of each level pushed
day, week,   YYYY-WNN.md, YYYY-MM.md          on a new task, <memory
month                                         scope="folder">; the rest:
episodes     ~/episodes/YYYYMMDD.md,          /recall-memories
day, week,   YYYY-WNN.md, YYYY-MM.md
month
people       ~/users/<id>.md                  pushed whole for the sender,
                                              <user-memory>
facts        ~/facts/*.md                     pulled: /recall-memories,
                                              then /find
index        ~/.claude/projects/-home-node/   read by Claude Code itself
             memory/MEMORY.md

/diary ──────────────────────────────────────────────► ~/diary/YYYYMMDD.md
session transcripts ─► /compact-memories episodes day ─► ~/episodes/YYYYMMDD.md
day files ─► /compact-memories <lens> week ─► YYYY-WNN.md ─► month ─► YYYY-MM.md
    one flat directory per lens; the five tasks run isolated

The diary is the folder seen from inside: what the agent did and decided. The episodes are the folder seen from outside: the exchanges, read off the session transcripts. One skill, compact-memories, writes every level above the first with one method: keep what a person said or corrected, drop what the agent concluded, end with the one thing to do next. Five scheduled tasks run it. Every group gets these tasks at creation, and each task runs isolated, so a compaction turn does not summarise its own work.

Three processes build what the ant reads

Three processes build what the agent sees, and each process builds the part that only it holds:

routd  buildAgentPrompt, routd/prompt.go:26 ───► RunRequest.MessageBatch
       <system-note> <system> <autocalls> <persona> <topic>
       <surface> <pane-context> <link-context> <messages>
         │
         │ POST /v1/runs
         ▼
runed  preparePayload, runed/render.go:177 ────► the guest's stdin prompt
       Topic session:  <user id>
       then routd's string unchanged
         │
         │ vsock, guest-init, stdin
         ▼
ant    buildSystemPrompt, ant/src/index.ts:49 ─► the system prompt
       SYSTEM.md, the package manual for codex and pi, PERSONA.md

ant    hooks/prompt_context.py, UserPromptSubmit ► additionalContext
       <knowledge layer="work">  <user-memory>
       <memory scope="folder"> on a new task

The <user id> tag names the sender and holds no content. The hook reads the file of the sender in users/ and adds it as <user-memory>. Bot rows never start a turn. When a tool returns history on purpose, each bot row carries bot="true". So the agent does not read its own past words as the words of another person.

The selected packages' manuals are combined at ~/.claude/CLAUDE.md, including the required core manual. The agent reads the file on each turn, so a section stays in it only for one of four reasons:

Everything else lives in skills. A test fails when the file points at a skill or a doc that does not exist.

If a group has a SYSTEM.md, that file replaces the base system prompt completely, and nothing merges into it. PERSONA.md follows it.

Each turn gets a new guest

The guest lives for one turn. runed starts it when a message arrives, the agent reads the prompt and replies, and the guest powers off. Nothing that matters lives inside the guest, only in the mounted folder. The next turn gets a new guest and a new kernel with the same folder.

A turn has one deadline, RUNED_RUN_TIMEOUT, and the default is 20 minutes. The timeout of the agent is 30 seconds shorter, and never below 60 seconds, so the agent can report before runed stops the guest. For a longer job, split it into turns, write checkpoints to ~/facts/ or ~/tmp/, or use a background task. Do not raise the timeout.

A grant gives capability, not the folder depth

The depth of a folder says nothing about what its ant can do. solo/inbox and corp/eng/sre/oncall start with the same rights: the role:member role. Every folder gets this role at creation, and it carries the messaging verbs and nothing more. Every other right is a row that a principal gave to the folder for a subtree. Examples are the rights to register groups, edit routes, open egress or schedule tasks. If an ant has no row for a tool, the ant does not see the tool at all.

The agent calls tools over one socket

The agent calls MCP tools, such as send or find_messages, over a unix socket. routd hosts one socket per group at ipc/<folder>/gated.sock on the host. For each turn, runed runs a small bridge that accepts the vsock connection of the guest and connects it to that socket. Inside the guest, the mcpServers config of Claude Code points at a local path. Every tool call is a JSON-RPC message over that socket. routd runs the same grant check on it as on a call to the matching REST endpoint.

Selected packages supply the next turn

Startup installs the release's core package. arizuko package sync updates selected package sources. The turn renderer mounts their skills, native hooks, tools and combined manual read-only; custom group skills stay writable. Package updates need no agent migration turn.

The folder stays and the guest goes

Stays
Everything under groups/<folder>/: the diary, facts, skills and the JSONL session transcripts in .claude/.
Goes
The guest, all files in /tmp inside it, and all process state in RAM. The own filesystem of the guest is a tmpfs overlay that power-off discards.

For this reason, an edit to the folder is enough. Change PERSONA.md on the host or through WebDAV, and the next guest uses the new persona without a restart or a reload.

Four pages continue from here