Node.js security releases (October 2026): update Node to 22.23.4, 24.22.1 or 26.12.1 before you use yokoten
yokoten
Docs Managing agents like a team

The guide

Managing agents like a team

Source: docs/guide.md · v0.1.2

The setup treats agents like staff on a line, each with a clear role and limited rights:

  • The owner (you) decides what gets built and when it ships. Only you approve a hard line, create release tags, and change repo settings. An area lead of another model may approve its area (records 0026 and 0047).
  • The lead is the session you talk to. It plans, splits the work, reviews, and opens pull requests through a bot, never with your account.
  • Workers are sessions the lead starts for one task each, in their own worktree under .worktrees/, with YOKOTEN_ROLE=worker. They change code and report back. They never push. A fix task also gets YOKOTEN_FIX_TASK=1. Then the guard and the commit check stop a change to a test file. The stop hook runs the brief’s done-when command. After three refusals the worker may stop. Its message to the lead starts with “NOT DONE: checks failing”. In a linked worktree, .yokoten-agent.json records checks. The lead reads that file. Its /api/state shows it as the commit check.
  • The bot is a GitHub App installed on the repo. It opens pull requests. If it has the Workflows permission, CODEOWNERS still sends every CI change to you, and a release waits for you in the protected release environment. A ruleset requires your review, so nothing it opens merges without you.

A worker runs its commands under the guard, so write the brief to fit it:

  • Start with cd <worktree> && cmd && cmd. The cd stands on its own, joined by && or ;: not in a subshell, a pipe or the background. Use no pipe after it.
  • No git -C: cd into the worktree first.
  • No VAR=value before a command, except TZ, CI, NODE_ENV, NO_COLOR, FORCE_COLOR, DEBUG, LANG, LC_ALL and the names yokoten.json lists in worker.settings.
  • If the task needs another setting, such as a test’s capture file, the lead makes it and hands the worker the file.

Seven skills carry the method. dispatch briefs a worker. inspect reviews one exact commit in three passes. rca finds the cause, adds a check, then spreads it. record writes decisions down. release picks the next version and prepares the changelog. The owner tags. merge merges through the bot and stops when the new commit is not UTC. yokoten adopts the practices. Three agents do the work: worker, reviewer (reads only), and verifier (runs, never fixes).

Who reviews what

Review follows risk. The tier on a pull request’s first line says how hard a mistake is to undo (record 0004):

Tier Kind of change Who reviews
C Standard: docs, wording, styles The lead
B Normal: other code, with tests An agent of another model reviews; the lead approves
A Hard to undo: owned files, or a change that makes a rule looser The owner, or an area lead of another model for a path that is neither a hard line nor a looser change

In yokoten’s own repo, .github/CODEOWNERS lists the owned files: the core rules, CODEOWNERS, CI that publishes or holds a secret, the bot token, guard config and package.json (record 0037). In a new project, init writes one for the lead-only paths. With “require review from code owners” on, the host asks the owner to review a pull request that changes one of them.

check --pr ties the two together. A pull request that changes an owned file fails unless its first line is tier: A. The paths set the lowest tier. The author can raise a tier, never lower it. The check reads CODEOWNERS from the base branch, so a pull request can’t edit the file to lower its own tier. Without a base commit, on a local run, it reads the working tree. The message says which one it read. No CODEOWNERS file, no check.

An approval is for one commit, and it must come from a code owner of the pull request’s changed files: the owner’s login approves as a collaborator, so the account never counts, only CODEOWNERS membership does. When the changed files name no owner, any owner named anywhere in CODEOWNERS counts. yokoten bot refuses gh pr merge of a tier A pull request whose newest code-owner review is not an Approve on the head. A newer changes-requested or dismissed review refuses too, while other reviews never count. When the head is newer, the merge goes on only for a clean merge of the base branch. The pull request’s own diff must be the same as at the Approve. Otherwise it refuses: the Approve is on ; commits with changes came after it; ask the owner to approve again. gh pr status through the bot shows the same check for tier A pull requests. Keep GitHub’s dismiss-stale-approvals setting on too, but yokoten never depends on a host setting a project may not have.

Area leads approve in their area

Record 0026 moves the lock for paths that are not a hard line from your code-owner review to yokoten’s merge check. An area lead approves with a GitHub review. The last line of the review body names the lead, its vendor and its model:

Approved by area lead Sonnet (Anthropic, Claude Sonnet 5.5)

GitHub records the commit the review was given on. A later commit needs a new review. The lead must be another model than every writer on the Made with line (record 0047). Claude Sonnet may approve work by Claude Opus, and a lead from another vendor may too. The check compares names, not spellings: claude-opus-5-5 and Claude Opus 5.5 are one model. When the Made with line names no writer, the check refuses. A file that .github/CODEOWNERS owns, and a looser change, still needs your approval. The release pull request changes package.json, so it always needs you. Until the diff tier check (#697) lands, a change to a test that already exists counts as looser; a new test does not.

Nothing here changes your repository settings. When you are ready, follow these steps.

  1. Check that .github/CODEOWNERS lists only the hard lines. In this repo it already does (record 0037): the core rules, .github/CODEOWNERS itself, CI that publishes or holds a secret, the bot token, guard config and package.json. If a path there is not a hard line, remove it: a path you remove becomes lead-approvable.
  2. Keep one required approval. A lead’s review is a real GitHub review, so keep “Required approvals” at 1. Do not let GitHub Actions approve a pull request.
  3. Keep the CI check required. The branch ruleset should still require the CI check that runs yokoten check --pr. The bot’s merge check is the second lock: it reads the lead approval before gh pr merge.
  4. Turn off “Require review from Code Owners” when the hard lines are safe. After step 1 the setting names only hard lines, so leaving it on still asks you only for those. Turning it off lets the bot’s check decide the rest.

yokoten doctor still expects code-owner review today. Expect that one step to note the change until it learns the new lock. When a merge is refused, the message names the reason and the next step: a lead of another model, a new review on the head, or the owner for a hard line or a looser change.

A reviewer finds the tier through labels, so check --pr also reads them. A pull request needs one type label and one tier label. A type label is a title type (feat, fix, docs, refactor, ci and the others) or a common name for one: bug, hotfix or incident for fix, feature or enhancement for feat, documentation for docs. The tier label is tier A, tier B or tier C (tier: B also counts). It must match the tier: line in the body. Each missing or wrong label gets its own message.

Server setup, step by step: docs/server.md.

Review bots you will see on pull requests

A review bot reads a pull request after you open it. It posts comments on the pull request page. You may see a walkthrough of the change, a risk level, a list of pre-merge checks, and a suggested fix. Some bots can push that fix.

Two kinds exist:

  • Commercial. The bot is closed source. The company runs it, and your code goes to the company’s servers. Many are free for public repos and paid for private ones.
  • Open source. You run the bot yourself, with your own model API key. You pay the model vendor for the tokens it uses, and the code stays on your machine.

Prices, licences and data handling change. This guide names no bot as a recommendation. Read the bot’s own page before you turn it on. A bot’s comment usually links to it.

yokoten works before the pull request, on your machine:

  • the guard stops a risky command, a secret or a private detail;
  • the inspect skill reviews the change in your agent session, at no extra cost;
  • the checks and the merge rules stop a merge or release you did not approve.

A review bot works after the pull request. The two fit together. The bot sees the pull request; yokoten sees the session that wrote it.

You do not need a review bot to start. yokoten’s own review is enough. Add a bot when your team wants a second reader on the pull request page. doctor never asks you to add one: it names yokoten’s own next step.

yokoten’s findings on the pull request page come from its own checks only. It does not repeat another bot’s findings or review the code a second time (#91).

Keeping pull requests up to date

Every merge leaves the other open pull requests behind. A conflict waits for nobody, so the lead runs yokoten pr sync after every merge. It merges each branch’s base branch in, runs typecheck and the tests of the touched files, and pushes as the bot. This runs after every merge, clean or resolved, and in UTC so the merge commit is too. It resolves only two mechanical conflicts: two added blocks at one spot, and two changed import lists. Anything else stops with the files and the hunks, and the lead hands them to a worker with the dispatch brief. Each report line names the base it merged. The list stops at 100 open pull requests, and the report says when it did.

The owner never resolves a conflict on GitHub. The web editor would commit as the owner, and the bot must be the one that pushes. Related: issue #320, the merge train.

The desk

A desk is a fenced place for one AI to do one task, the same for every vendor (#637). Start one with an issue number or a task in quotes:

yokoten desk 42
yokoten desk "fix the login bug" --agent codex

yokoten desk makes the desk ../<repo>-desks/<n>/ (its own clone, branch desk/<n>), writes a brief, and starts the AI there inside the fence. It never pushes; --pr pushes the branch and opens the pull request. When the AI exits, it runs the check and the tests in the fence and shows the evidence. With no words, it lists your desks.

The older form, for scripts, starts any command in a fenced clone you made yourself:

git clone ../yokoten ../desks/42
yokoten desk claude-code ../desks/42 -- claude -p "$(cat brief.md)"

The fence is one contract. Each system builds it its own way:

Rule In the desk
Writes Only in the desk folder (.git too, so the worker commits) and the desk’s own state folder for the agent. .git/config and .git/hooks stay read-only
Reads Everything else the agent can see is read-only
Other projects Not visible. Your home and the desk’s parent folder are hidden. On Linux, /run is hidden too: its sockets lead out, and on WSL2 one of them starts Windows programs. Node, the agent program and yokoten are shown again, read-only
Your agent state Not visible. Your ~/.claude, ~/.codex, ~/.grok and Muse folders stay hidden: no settings, hooks, memory, MCP config, history or sessions
Child processes Work, with pipes, so tests can start programs
Network Commands get no network of their own. The proxy outside lets through only the agent’s API hosts. Any command inside can use the proxy too, so a command can reach those hosts
Environment YOKOTEN_ROLE=worker and TZ=UTC. No token or key from your shell (see What an agent’s environment holds)
Team The team folder is read-only. Only the agent’s own outbox in it is writable (see Messages between agents)

The desk must be its own git clone. A worktree’s git folder lives in another checkout, which the desk hides, so a worktree is refused.

Who the agent is. The desk reads the model and provider from the agent’s own config: for Codex, model and model_provider in config.toml. The kanban shows that model. When its vendor is not the harness’s own, the session starts with one line that says who the agent is, and that the harness is its tool. A DeepSeek model runs through Codex, whose prompt names OpenAI; without the line, the worker signs its work as OpenAI.

The desk’s state folder. Each desk gets its own state folder for the agent, next to it: <desk>.state/<agent>. The desk points the agent there with the agent’s own variable. Only the login file is copied in from yours. A newer login of yours is copied again on the next start. Remove the state folder with the desk.

Agent <agent> Points the agent at its state with Login file copied API hosts Command inside the desk
Claude Code claude-code CLAUDE_CONFIG_DIR .credentials.json api.anthropic.com, and platform.claude.com to renew a login claude -p "<brief>"
Codex, and DeepSeek through Codex codex CODEX_HOME auth.json api.openai.com, api.deepseek.com codex exec --sandbox danger-full-access "<brief>"
Grok grok GROK_HOME auth.json api.x.ai grok -p "<brief>"
Muse Code muse XDG_CONFIG_HOME, XDG_DATA_HOME muse/auth.json api.meta.ai muse exec "<brief>"
  • Claude Code renews a login at platform.claude.com. A token from claude setup-token in CLAUDE_CODE_OAUTH_TOKEN, or an API key, needs no renewal and passes in as a login.
  • Codex runs with --sandbox danger-full-access inside the desk. Its own workspace-write sandbox makes .git read-only and blocks the pipes that tests use. The desk is the fence instead. Your config.toml stays out: give a provider with -c on the command. A ChatGPT login also needs --host chatgpt.com,auth.openai.com.
  • Grok: turn off --sandbox inside the desk if it stops a commit or a test.
  • More hosts: --host <host> lets the agent reach another API host on port 443. Separate several with commas.
  • More files: --read <path> shows another path, read-only. A path that would open your home, a hidden folder or your own agent state is refused.

The proxy. Each of the four agents names HTTPS_PROXY in its program, and the desk sets HTTPS_PROXY, HTTP_PROXY and ALL_PROXY in both cases. It also sets NODE_USE_ENV_PROXY=1, so Node’s own fetch uses the proxy too. The proxy takes CONNECT only, to a listed host, and never reads the traffic. It keeps at most 64 tunnels open, and closes a tunnel that is still for 10 minutes. When it refuses a host, the desk names the host at the end.

Review from outside. The worker writes in the desk, so treat the desk as untrusted. Do not run git or any other command inside the desk before you check the work. Fetch the branch into your own checkout, then review it there:

git fetch ../desks/42 HEAD:refs/desks/42
git diff main refs/desks/42

.git/config and .git/hooks are read-only in the desk, so a worker can’t plant a hook or an alias there. Set an upstream or other config from outside, before you start the desk.

The commit check and your private marks. The commit check reads your private-marks file. An agent that can read the file can also print it. So the desk hides it by default, and the check in the desk does not see your marks. Until marks are kept as hashes (#665), run the check yourself, outside the desk. Check out the fetched branch in your own checkout, then run yokoten check there; it checks the whole tree. --marks shows the file in the desk, read-only. Use it only for an agent you trust with the words.

Systems.

The fence is Anthropic’s sandbox-runtime (srt), the sandbox Claude Code uses. yokoten runs srt from PATH when it is the pinned version, 0.0.81. Otherwise npx gets that version once and keeps it in npm’s cache. It is not a dependency of yokoten. When the sandbox can’t start, the desk says why in one line and starts nothing.

  • Linux and WSL2: install bubblewrap, socat and ripgrep. On Debian and Ubuntu: sudo apt install bubblewrap socat ripgrep. The desk gets its own empty network, and only the agent’s API hosts pass srt’s proxy.
  • macOS: sandbox-exec, built in. Not yet run on a Mac: run node --test test/agents/desk/start.test.ts there once to check the contract.
  • Native Windows: no desk. yokoten desk starts nothing and says to use WSL. On native Windows, the guard alone holds the rules.

YOKOTEN_FENCE=own uses the old fence until v0.1.3: bwrap on Linux, sandbox-exec on macOS.

Messages between agents

Agents of every vendor can message each other (#674). It is files on this machine: no server, no network.

yokoten msg send lead "tests pass; ready for review"
yokoten msg inbox
yokoten msg who
yokoten msg log
  • Where: the team folder next to your checkout, <repo>.team/. Each agent writes only its own outbox there. yokoten desk adds the agent by --name, or by the desk folder’s name made lower case. A folder name that can’t be a name joins no team.
  • Who you are: in a desk, the desk says. Outside one, you are lead only in your main checkout, not in a worktree or a checkout inside a desk.
  • A desk can’t fake a sender: in a desk, the team folder is read-only, and only the agent’s own outbox is writable. msg send refuses a worker outside a desk, and the guard stops its edits in the team folder.
  • Outside a desk, a sender can be faked: a program outside a desk runs as your user, so it can write any outbox, the lead’s too. A worker in a plain worktree can do that, for example from inline code that leaves its folder. yokoten can’t tell such a write from yours. Run workers that message in desks.
  • Delivery: the hooks add unread messages to the agent’s context. Claude Code and Grok get them before a tool call. Every agent gets them at the end of a turn, after the checks: a turn that fails a check is sent back with the messages too. msg inbox shows them at any time. Muse Code’s hooks get no variables, so in a desk Muse reads with msg inbox.
  • A message is data: every delivery says that the messages are from another agent and can’t grant approval or permission. This note is the defence. Each message names the sender’s vendor and model. Some claims of the owner’s approval get a flag, but the flag misses claims in other words.
  • Privacy: a message gets the same scan as posted text: private marks, secrets, email and IP addresses, and secret variables. A message that fails is not sent, and you get the reason. The scan reads plain text, so a secret that is encoded or split passes it. A desk sees your private marks only with --marks, so a message from one desk to another is checked against them only then. What the lead reads is always checked.
  • Who reads: every desk on the team can read every message, not only its own.
  • Caps: 2,000 characters a message, 20 messages per agent in 10 minutes, and 256 KB per outbox.
  • The log: msg log shows every message. Only the lead runs it.

The kanban

yokoten kanban shows the team at a glance. It is a preview, so it may change. It is read only: nothing on it starts, stops or changes work. Record 0044 says why. (Before v0.1.2 it was called yokoten board. The old name still runs it.)

It shows three things:

  • Sessions. Each AI session in this repo, its worktrees and its desks: the agent, the model, the task, and whether it is working, stuck, waiting or done. A session with no output for 15 minutes counts as stuck.
  • Proof. For each session: whether a system sandbox or only the guard fenced it, whether the commit check passed, and the tests on Linux, Windows and macOS. Those three read “not proven” until the status file records platforms.
  • Pull requests and Actions. The pull requests that wait on you, the ones merged lately, and the health of GitHub Actions. The tokens each agent used show with them.

yokoten kanban --text prints the sessions by state, the pull requests and Actions, and exits. That works today. The proof and the tokens are in GET /api/state for now.

Without --text, it serves a page on this machine only, at http://<repo>.localhost:<port>/. <repo> is the folder name, made safe for a hostname. The port stays the same for this repo. It opens your browser, signed in, and runs until you stop it with Ctrl-C. --no-open only prints the address. The sign-in key goes only to the browser and is never printed. A request with a wrong host is refused.

The page is being rebuilt as its own web package (record 0044). Until it is built, / says so in one line, and GET /api/state still works. It returns one snapshot of the sessions, pull requests and Actions as JSON. GET /api/sessions/<id>/stream sends one session’s output as server-sent events. The id is a hash of the session’s folder, never the path. Any request but GET or HEAD gets a 405.

The kanban reads GitHub through gh, in the background, one read at a time. When a read fails, it keeps the last lists. The guard checks each agent, and pushes and merges still go only through the bot.

Agents show by themselves. Start an agent in this repo, in one of its worktrees, or in a desk (yokoten desk), and yokoten’s hooks keep its status in .yokoten-agent.json at that checkout’s root. This works for every vendor:

  • The session start writes vendor, model, role (lead in the main checkout, engineer for a worker, by the guard’s own rule), worktree, state: working and started. started is written once. Grok has no session-start hook: its first call writes them.
  • Each allowed tool call sets doing and updated, at most once every 5 seconds.
  • A refused call sets blocked. The next allowed call clears it.
  • The stop sets state: done with checks for a worker, waiting for the lead.

The hooks write the file only in a repo with yokoten.json. A worker never writes the main checkout’s file: that one is the lead’s. The first write adds the file to the repo’s own .git/info/exclude, so git status never shows it. A desk is a plain clone, so git worktree list misses it: yokoten desk notes it in the repo’s git folder, and the kanban shows it too.

An agent may also write the file itself, for task; the hooks keep the fields they do not set. It leaves by removing the file. The format is open: other tools may read it too. One file per worktree.

Field Means
name The agent’s name. Without it, the worktree’s folder name is used
vendor Which vendor it runs on, such as claude-code, codex or grok
model, effort The model and effort, such as opus and medium
role lead, engineer, reviewer, verifier or helper
task What it works on, such as PR #615 Run rules locally
worktree The worktree it works in
doing What it does now, in a few words
state working, waiting (it needs you) or done
checks passing or failing, from its last test run. It is in GET /api/state as the commit check
updated When it last wrote, such as 2026-10-22T10:00:00Z. Quiet for 15 minutes shows as stuck
started When this work started, such as 2026-10-22T08:00:00Z. Without it, the file’s birth time is used, then updated
blocked A guard stop that needs a decision, in one line

Every field is text. Every field may be missing. A file that is not a JSON object is skipped. A state or checks outside the words above is ignored.

What agents write shows only as text, never as page code, and is masked like a session. The kanban loads nothing from the internet. Its interface is /api/state; the page is only one view of it.