← All writing.md

One rulebook for two agents: the harness behind our second brain

October 12, 2026 · 10 min read

Two desks side by side in a warm study, one open notebook shared across both

We run two AI agents inside one notes vault. Claude Code works on a laptop and on a small cloud server. Hermes, an open-source agent, lives on the same server, answers Telegram messages, and runs scheduled jobs like the morning stand-up. Both read and write the same markdown files.

Last week a post by @beamnxw on harness engineering made the rounds. Its argument: a model finishes long tasks when the setup around it defines the outputs, the evidence and the handoff before the run starts. We audited our own setup against it and found a problem we hadn't noticed. Our two agents were following two different rulebooks.

Why bother with a harness

The models were rarely the problem. The failures we could name came from the setup around them:

  • The stand-up agent wrote a line about a conference prize that appeared nowhere in its input. Nobody checked, so it went out.
  • A deploy run from a fresh copy of a repo used rsync --delete and removed files that only existed on the live server.
  • An agent cleaning up “orphaned” folders deleted 63 GB of video footage that belonged to live projects.

Each of these had a written rule against it somewhere. Written rules are suggestions. The agent reads them, mostly follows them, and on a bad day doesn't.

A harness is everything around the model that turns suggestions into structure: the instructions it loads, the tools it may call, the checks that run before it finishes, and the notes it leaves for the next session. We rebuilt ours around five pieces, and each one had to work in both agents without being written twice.

1. One instruction file, imported everywhere

Claude Code reads CLAUDE.md. Hermes reads AGENTS.md, and it loads only one project file per session, so it never sees CLAUDE.md when AGENTS.md exists. We had both files, and over a few months they drifted. Claude never saw the rule that every build needs a dev log. Hermes never saw the rule about formatting text you'll paste into an email.

The fix is boring and it works. AGENTS.md is now the only place rules live. CLAUDE.md shrank to a few lines ending in @AGENTS.md, which Claude Code treats as an import. Rules that only matter in one folder, like the facts about our app's deployment, moved into an AGENTS.md inside that folder, with a one-line CLAUDE.md next to it. Both agents load those folder files only when they start working in that folder.

One honest note: this did not shrink what the agents load at startup. It grew. Hermes now loads about 17,000 characters of instructions instead of 13,000, and Claude went from 6,000 to roughly 18,000, because it finally sees rules it was missing. We traded tokens for consistency, and we'd make that trade again.

2. One guard script, two agents

Both agents support hooks: small programs that run before a tool call and can block it. What surprised us is how close the two formats are. Claude Code sends a JSON payload with the tool name and its input, and blocks when the script exits with code 2. Hermes sends nearly the same payload and accepts a Claude-style {"decision": "block", "reason": "..."} reply. So one Python script, guard.py, protects both. It checks which agent called it and answers in that agent's format.

A brass gate latch on a heavy wooden door, lit by a single warm lamp

It blocks a short list, and every item maps to something that went wrong or nearly did:

  • Printing or copying .env and credential files, because keys should never land in a transcript. Loading them with source .env still works.
  • rsync --delete without a dry run first: the deploy that wiped live files.
  • Deleting the video project's blob folder: the 63 GB loss.
  • git push --force, because several sessions share one repo.
  • Paths owned by other system users. Some keys belong to a separate user on purpose.

The guard splits a shell command on ;, && and pipes, and judges each piece by its first word. That is why set -a; . ./.env; set +a; python3 gen.py | head passes while cat .env doesn't. It has 74 tests covering both payload formats.

It is not a sandbox. A Python script the agent writes can still open any file its user can read. Hooks catch the agent's own tool calls, and the real protection for anything valuable is a separate system user with its own permissions. We'd rather say that plainly than oversell a regex.

The guard went live mid-build and immediately blocked our own setup step, an echo that wrote a test value into a .env file. That's the correct behavior, and it was a nice moment.

3. A reviewer that only reads

A writer is a poor judge of its own work. So we added a fact-check skill: give it an output file and the sources it was written from, and it returns a table with one row per factual claim, marked verified, incorrect or unresolved, each with the source line it checked. It never edits anything.

In Claude Code it runs as a subagent restricted to read and search tools. Hermes can't restrict a sub-task's tools (children inherit the parent's), so there the same skill runs in-session with read-only as an instruction. The morning stand-up now runs it before saving and drops any bullet it can't trace back to its input.

An editor's desk at night, a magnifying loupe resting on a stack of handwritten pages

We tested it on the design document for this very system. Of 41 claims, 37 checked out, three couldn't be confirmed from the sources we allowed, and one was wrong. The document said both hooks fail safe when they hit an unexpected error. The reviewer read the code and found that the guard didn't: a payload in an unexpected shape would crash it. We fixed it and added a test. A reviewer that finds a real bug in its first run has earned its place.

4. A handoff note every agent can read

Long work spans sessions, machines and agents. Each project's dev log now starts with the same six-line block:

## Resume
- **Task:** what this project is doing right now
- **Outputs:** the paths that hold the current work
- **Completed:** finished stages and the checks that passed
- **Decisions:** confirmed choices, each with where it was decided
- **Open issues:** missing evidence, blockers, open questions
- **Next action:** one concrete step the next agent can start on

To resume, any agent reads the block, opens the files it names, and starts on the next action. Because the format is fixed, a script can read it too: the morning stand-up now lists every project's next action and open issues without anyone writing a summary.

5. Define “done” before you start

Both agents now have a /goal command that keeps working until a judge model decides the goal is met. The judge can only check what you told it to want, so we write every long task as a five-part contract: the outcome (files that must exist), the verification (the check that proves it), constraints, boundaries, and when to stop. Hermes uses these same five fields natively. The agent prints its output paths and check results at the end, because the judge only sees the conversation, not the disk.

Scheduled jobs get the short version. Each prompt ends with a “Done when” line and an instruction to reply BLOCKED: <reason> and stop if something fails. A broken morning run should arrive as a failure, not as a half-finished summary that looks complete.

A wall clock and an open ledger on a sunlit desk, a capped pen set down beside it

One more hook enforces our oldest rule. When an agent tries to finish after changing scripts, skills or server config without writing a dev log entry, a second script sends it back once to write one. It nudges a single time, so a false alarm costs one extra turn.

How to set this up yourself

You need a repo or folder that more than one agent works in. Then:

  • Pick one canonical instruction file. Use AGENTS.md, since most agents read it. Make CLAUDE.md contain only @AGENTS.md plus anything Claude-specific. Move folder-only rules into that folder's own AGENTS.md.
  • Write down what has actually gone wrong. Your guard rules should come from incidents, not imagination. Five rules you understand beat fifty you don't.
  • Write one guard script. Read JSON from stdin, take tool_name and tool_input, decide. For Claude Code, block with exit code 2 and the reason on stderr. For Hermes, print {"decision": "block", "reason": "..."}. Wrap everything so any error exits 0, then write tests for both formats.
  • Wire it into each agent. Claude Code: a PreToolUse hook in .claude/settings.json. Hermes: a hooks: pre_tool_call entry in its config.yaml, plus the exact command string in ~/.hermes/shell-hooks-allowlist.json. Hermes skips unapproved hooks in background runs.
  • Share skills from one folder. Put them in .agents/skills/. Symlink .claude/skills to it, and list it under skills.external_dirs in Hermes.
  • Add a read-only reviewer to whatever output you read without checking.
  • Add the Resume block and the five-part contract to your instruction file, and test each piece live by asking the agent to do the forbidden thing.

Instructions for your agent

If you'd rather hand this to your own agent, paste the block below into a session in your repo. It's written for an agent to follow.

Set up a shared agent harness in this repository so Claude Code and Hermes follow the same rules.

1. Instructions: make AGENTS.md the single source of rules. Merge any rules that exist
   only in CLAUDE.md into AGENTS.md. Replace CLAUDE.md with a short header and the line
   "@AGENTS.md". For rules that apply to one folder only, create <folder>/AGENTS.md and
   <folder>/CLAUDE.md containing "@AGENTS.md". Show me the diff before deleting anything.

2. Guard: create .agents/hooks/guard.py. Read a JSON payload from stdin. If
   hook_event_name is "PreToolUse" (Claude Code), block by printing the reason to stderr
   and exiting 2. If it is "pre_tool_call" (Hermes), block by printing
   {"decision": "block", "reason": "..."} and exiting 0; otherwise print {}.
   Shell tools are Bash / terminal (input key: command). File tools are Read, Edit, Write /
   read_file, write_file, patch (input key: file_path or path). Ask me which incidents to
   guard against; start with: printing .env files, rsync --delete without -n, and
   git push --force. Split shell commands on ; && || | and judge each segment by its
   first word. Any exception must exit 0. Write tests for both payload formats and run them.

3. Wiring: add a PreToolUse hook running the guard to .claude/settings.json. Add
   hooks.pre_tool_call (matcher "terminal|read_file|write_file|patch|search_files") to the
   Hermes config.yaml, and add the exact command string to
   ~/.hermes/shell-hooks-allowlist.json. Run: hermes hooks doctor

4. Skills: create .agents/skills/. Symlink .claude/skills to it. Add it to
   skills.external_dirs in the Hermes config.

5. Reviewer: create .agents/skills/fact-check/SKILL.md. It takes an output path and its
   sources and returns a table (claim, verdict: verified / incorrect / unresolved, source,
   correction). It never edits files. For Claude Code, add .claude/agents/fact-checker.md
   with tools: Read, Grep, Glob, WebFetch, WebSearch.

6. Handoff and done: add to AGENTS.md (a) a "## Resume" block format with Task, Outputs,
   Completed, Decisions, Open issues, Next action, required at the top of every project
   dev log, and (b) a completion contract format with Outcome, Verification, Constraints,
   Boundaries, Stop when, used for /goal and scheduled jobs.

7. Test live: in a new session of each agent, try to cat a dummy .env file and run
   rsync --delete on two scratch folders. Both must be blocked. Report the results.

What we'd do differently

We'd start with the guard and the reviewer, because they caught real problems on day one. The single instruction file is the least exciting piece and the one we'd least want to give up. The stand-up's new fact-check step runs for the first time tomorrow morning, on a small and fast model, and we don't know yet whether it's strong enough to catch its own mistakes. We'll find out.

Argo Writer Agent

Start your 750 words today

Argo turns a daily entry (typed, spoken, or filmed) into clarity you can actually see. Private, encrypted, and built entirely from your own life.

Download on the App Store