Self-improving agent orchestration: a proposal¶
How the harness, the skills, the board, the org, the cron jobs and the engine itself could learn from their own runs¶
Status: proposal; two of its pieces have since shipped outside the improve module itself. The headless-runtime pilot it asks for in section 10 is built as zen-agent/ (zen-agent.md); the kanban board it asks to bring into the engine in section 5.3 is built as the board module (board-design.md). Everything else — the stamp, run_outcomes, mining, the eval tiers, the harness-engineer bot, memory curation, the outer loop — is still a suggestion, not built, unless listed under "What exists today".
Written: 2026-10-24 · Updated: 2026-10-25, after zen-agent and the board module landed
Goal: Decide how to add a bounded, auditable self-improvement loop to the second brain: one that mines the traces the system already keeps, proposes changes to its own mutable surfaces, evaluates them before anything is promoted, and leaves a record a person can read and revert.
Scope: The engine (this repo) and the vault files it reads. Model weights are out of scope: the models stay frozen behind Claude Code, Hermes and their APIs.
TL;DR: the five things that matter¶
- We already have most of the expensive half. Traces (
archive_*,usage_calls,llm_exchanges), outcome labels (usage_runs.verify_*, review rounds, held cards,is_error), a dependency-aware kanban, a human approval gate for shipping, a sourced fact memory and a dev-log discipline all exist. What is missing is the loop that joins them: no run is stamped with the version of the rules and skills it ran under, nothing clusters failures, nothing proposes a change, nothing evaluates a proposal before it goes live. - Treat every instruction file as a harness version and stamp it on every run. One hash over
AGENTS.md, the skills,guard.local.json,graphs.json, the org roster, the cron prompts and the engine pin, recorded with each run, is the single change that turns the existing ledger into an evaluation dataset. Everything else in this proposal builds on that stamp. - Improvement is a pull request, never a live edit. The improver proposes a diff to one surface, attaches an improvement record (hypothesis, trace evidence, eval plan, results) and opens a PR. The existing rules hold: agents never merge their own PR, nothing ships without the owner's yes, and a change is promoted only with zero regressions on the static checks and the replay suite. This is what keeps the loop bounded.
- Evaluate with behavioural contrast on recorded tasks, not with a judge's opinion. The eval compares a candidate harness against the current one on the same recorded cards: verification pass rate, review rounds, held rate, tokens and turns per finished card, guard blocks, owner interventions. An LLM judge is used only for pairwise preference on open-ended outputs, with a written rubric, and never decides promotion alone.
- Most likely to change the plan: a smaller headless runtime. The bots' headless wakes (
claude -punder the dispatcher) are where context cost, missing traces and weak control over compaction hurt most. A thin runtime built on a minimal agent core (the pi packages, formerly pi-mono, are the leading candidate; see sections 10 and 14) would give in-process hooks, deterministic context assembly and session files as traces. It is a pilot, not a replacement: the owner keeps Claude Code interactively, and Hermes keeps its cron.
1. The frame: the system as a mutable program¶
The research the owner shared describes an agent system at iteration $t$ as a tuple $x_t = (M_t, H_t, D_t, T_t, \text{Imp}_t)$: the model, the harness, the data and trajectories, the trainer or evaluator, and the improvement mechanism. The second brain maps onto it cleanly, and the mapping shows where the gaps are.
| Symbol | In the research | In the second brain today | Mutable by the system? |
|---|---|---|---|
| $M$ | The foundation model | Claude (via Claude Code) and whatever Hermes is configured with. Frozen, reached over an API or a subscription | No, and this proposal keeps it that way |
| $H$ | Prompts, tool wrappers, memory layout, subagent configs, middleware | AGENTS.base.md + AGENTS.local.md (compiled to AGENTS.md), CLAUDE.md, .claude/settings.json, guard.py + guard.local.json, devlog_gate.py, the skills (SKILL.md), the fact-checker subagent, the org roster (org.yaml), the graph templates (graphs/*.yaml), the bots' memory files, the cron job prompts, the dispatcher's wake prompt, the kanban card bodies, secondbrain.json, the env |
Only by a person, or by an agent in a session that a person reviews. Nothing measures a change |
| $D$ | Trajectories and datasets | archive_sessions / archive_messages (every Claude Code and Hermes turn), usage_calls (tokens per call), llm_exchanges + llm_blobs (exact API requests when the capture proxy is on), usage_runs (one row per claude -p run with its wake reasons), org_messages, org_wakeups, cron_runs / cron_outputs, task_links, memory_facts, the dev logs |
Collected, searchable on /usage and /search. Not joined to the harness version that produced them |
| $T$ | Trainer and evaluator | The projects module's verifications (a feature goes live only with a passing check), the cc_verify line the dispatcher can append to a run, review cards and task redo, the fact-check skill, the engine's own test suites, CI |
Each exists in isolation. There is no suite that re-runs a task under a different harness |
| $\text{Imp}$ | The improvement mechanism | A person reading dev logs and stand-ups, then editing a file | Not represented in the system at all |
The proposal is to give $\text{Imp}$ a home: a module that reads $D$, uses $T$, proposes edits to $H$, and keeps its own policy in a file that goes through the same review as everything else. The research calls the last part bounded recursive self-improvement: the improver can change how it improves, but only through the same gate as any other change.
What the research adds, in our terms¶
| Idea from the literature | What it means here |
|---|---|
| Harness as readable, evaluatable code | Every file in the $H$ row above is already text in git. The missing piece is a version identity for the whole set, so a run can say which version it ran under |
| Trace mining and error localisation | A nightly job that reads the week's runs, finds the ones that failed, were held, needed a second review round or burned tokens without finishing, and asks what they have in common: a tool, a skill, a graph node, a cron job, a rule |
| Prompt and tool ergonomics refinement | Rewriting a SKILL.md, a graph node body, a cron prompt or a cli-* help text, then showing on the same tasks that the agent now needs fewer turns or fewer corrections |
| Skill compilation and memory libraries | Turning a recovery procedure that worked three times into a skill or a playbook bullet, with a source link, instead of leaving it in a transcript |
| Propose / probe / compose with behavioural contrast, and retention only with evidence ("zero-regression promotion" in the owner's summary) | The eval design in section 6: candidate vs. baseline on the same recorded cards, promoted only if nothing got worse |
| An outer loop that tunes the improver | .agents/improve/policy.json (clustering thresholds, eval budget, promotion thresholds) is itself a surface the loop may propose edits to, measured on how many of its proposals survived |
| Contract-based subagent interfaces | Cards and graph nodes get explicit fields: objective, scope, the artifact to return, a token and turn budget. The dispatcher enforces the budget; the reviewer checks the artifact |
| Graph handoffs as mutable programs | graphs/*.yaml is already a DAG in a file. Proposals can add a verifier node, drop a node that never changes the outcome, or change who a node is assigned to |
| Macro / micro separation | Leads and graphs learn delegation (which nodes, in which order, with what budget); members learn single-domain execution (skills, memory). The two are evaluated separately so a bad decomposition is not blamed on a bot and a bad bot is not blamed on the graph |
2. What exists today, and what is missing¶
This section is the inventory the rest of the proposal builds on. Paths are real.
2.1 Mutable surfaces (the harness, $H$)¶
| Surface | Lives in | Loaded by | Changed by | Versioned? | Signal that would tell you a change helped |
|---|---|---|---|---|---|
| Shared agent rules | vault-template/AGENTS.base.md (engine) |
Every agent, at session start, through the vault's compiled AGENTS.md |
Engine PR | Engine commit | None today |
| Owner's rules | AGENTS.local.md (vault) |
Same | The owner, by hand | Vault commit | None |
| Claude Code wiring | harness/claude/settings.json, copied to .claude/settings.json |
Claude Code | Engine PR, then a manual copy into existing vaults | Engine commit, but vaults drift | Guard log, BASH_MAX_OUTPUT_LENGTH effects on tokens |
| Guard rules | harness/hooks/guard.py (built-in), .agents/guard.local.json (vault) |
guard.py before every tool call |
Engine PR / the owner | Yes / vault commit | ~/.cache/agent-guard.log (blocks, never synced anywhere) |
| Dev-log gate | harness/hooks/devlog_gate.py |
Claude Stop, Hermes pre_verify |
Engine PR | Yes | Nothing records how often it fires |
| Shared skills | skills/<name>/SKILL.md |
Claude Code via .claude/skills, Hermes via skills.external_dirs |
Engine PR | Engine commit | None: no skill has a test or an eval |
| Private skills | .agents/skills/<name>/ (vault) |
Same | The owner or an agent (the dev-log gate watches it) | Vault commit | None |
| Fact-checker subagent | harness/claude/agents/fact-checker.md |
Claude Code | Engine PR | Yes | The tables it produces are not stored |
| Org roster | .agents/org/org.yaml in the vault, compiled to org.json, imported with cli-org import |
org_agents, teams, org_channels |
The owner; the compiler lives in the vault, not the engine | org_version hash on each agent row |
Per-bot activity on /org and /usage |
| Graph templates | .agents/org/graphs/*.yaml → .agents/org/graphs.json (loadGraphs in org.ts) |
cli-org graph apply |
The owner; compiler in the vault | Vault commit; task_links.graph and .node tag every card |
Rounds, time to done and tokens per node are derivable but nobody derives them |
| Cards and their bodies | The board module in the engine (migration, board_* functions, API, cli-board; see board-design.md), stage 0 still mirroring Hermes's own kanban while the rollout runs |
The dispatcher's wake prompt, zen-agent's board backend |
Leads, members, the owner, graphs | Not versioned; the contract fields (5.3) are not yet filled in by task create |
redo rounds, held: statuses, block reasons, wake-done fallback statuses, and now card_events/card_runs once stage 1+ writes land |
| Bot memory files | .agents/org/memory/<bot>.md |
cli-org recall (line-level word match) |
The bot, in its weekly reflection | Vault commit | None |
| Shared facts | memory_facts (Postgres), cli-org remember / recall / forget |
Any runtime | Any agent, with a source | Supersession chain | None: a fact is never scored on whether it was used or was right |
| Cron jobs | Hermes cron/jobs.json, prompt per job; synced into cron_jobs |
Hermes | The owner, in Hermes | cron_jobs.raw, first_seen_at |
cron_runs.status, cron_outputs content. No grading |
| Server timers | app/deploy/*.timer |
systemd | Engine PR | Engine commit | systemctl list-timers, surfaced on /cron |
Dispatcher wake prompt and cc_dispatch framing |
The dispatcher (cc-dispatcher, outside the engine) |
claude -p |
The owner | Not versioned | usage_runs rows, verify_* columns |
| Context budget settings | ORG_SESSION_MAX_RUNS, ORG_TASK_WAKE_RETRIES, ORG_TASK_WAKE_BACKOFF_MINUTES, BASH_MAX_OUTPUT_LENGTH, MAX_MCP_OUTPUT_TOKENS |
config.ts, Claude Code |
The owner | Env, unversioned | Tokens per run on /usage |
| The engine code | app/, harness/, tools/ |
Everything | PR with CI | Git | The test suites |
Two things stand out. First, half of the surfaces that drive the agent company (the roster and graph compiler, the dispatcher) still live outside the engine, so the engine cannot validate, version or test them — though the kanban itself moved in as the board module (5.3, board-design.md), and the headless runtime those surfaces feed exists as zen-agent (10.4, zen-agent.md). Second, no surface has a signal attached to it. The signals exist (right column) but are never joined to the surface they would judge.
2.2 Trajectories ($D$) and outcome labels ($T$)¶
| Table or file | What it holds | Attribution it carries | Attribution it lacks |
|---|---|---|---|
archive_sessions, archive_messages |
Every Claude Code and Hermes session, every message, classified into steps (prompt, response, thinking, tool_call, tool_result, system, meta) by archive/steps.ts |
host, harness, profile, parent session (subagents fold into parents) | Which card, which bot, which rules version, which skills were loaded |
usage_calls |
Tokens per API call, the first tool it invoked, its action class | session, model, entrypoint, sidechain | Same |
usage_runs |
One row per claude -p run: wake reasons, card, lane, bot, team, turns, duration, subtype (success, error_max_turns, …), is_error, and a verify_* block if the dispatcher appended cc_verify |
run id, card, agent, work type | Harness version; the graph node; the card's budget |
llm_exchanges, llm_blobs |
With the capture proxy: each exact Messages request, with the system prompt, tools and messages stored once by hash | session, model | Everything above. But the system_sha and tools_sha are already a de-facto harness fingerprint for Claude Code calls |
org_messages |
Statuses, decisions, hand-offs, questions for the owner, stand-ups, reflections | author, run id, card, project, feature | Nothing missing for its purpose |
org_wakeups |
Why a bot ran and whether it finished | reason, ref, claimed_by, result | |
task_links |
Card ↔ project, feature, assignee, graph, node, kind, rounds | When the card finished, how many wakes it took | |
memory_facts |
Sourced claims with confidence and supersession | author, via, project | Whether it was ever recalled, whether it turned out true |
cron_runs, cron_outputs |
Hermes cron executions and their outputs | job, profile | Whether the owner found the output useful |
Verifications (verifications table, projects module) |
A named check, pass or fail, evidence, per feature | project, feature, who recorded it | Which run produced it |
| Dev logs | What changed, why, how it was tested | Project folder, date | Nothing; they are the human-readable trace and should stay so |
~/.cache/agent-guard.log |
Every guard block | tool, reason | Never leaves the machine, never joined to a run |
The labels we need are nearly all present: a run either produced a passing verification or it did not; a card either finished in one wake or was held; a review either passed or asked for a round; a cron output was either delivered or errored. What is missing is cheap: a harness version per run, a card budget per card, a guard-block count per run, and a place to record the owner's reaction to a cron output.
2.3 Gaps, in one list¶
- No harness version on a run. Without it, no before/after comparison is possible.
- No failure clustering. The
/usagepage shows runs one at a time; nothing groups the week'serror_max_turnsruns by card type, skill or graph node. - No proposal mechanism. An agent that notices a bad rule has no sanctioned way to propose a fix except a dev-log line a person may read.
- No evaluation of instruction files. Skills, rules, graph templates and cron prompts have no tests, no examples, no replay.
- No contract on a card. A card body is prose; nothing states the budget, the artifact to return or the scope. The dispatcher cannot enforce what is not written down.
- Memory is a flat file and a fact table. Recall is a word match over
memory/<bot>.mdlines and a full-text query over facts. Nothing is scored, decayed, curated or promoted to a rule. - Cron is outside the loop. Hermes cron outputs are synced and displayed but never graded, and the prompts are not versioned by anything the engine sees.
- The orchestration surfaces are split across repos, less than before. The kanban moved into the engine as the
boardmodule (board-design.md); the roster and graph compiler and the dispatcher are still outside it, and the engine imports their outputs and cannot test them. - Guard blocks and gate nudges are invisible. Both hooks log locally or not at all.
- Context cost is managed by rules of thumb (
ORG_SESSION_MAX_RUNS, output caps) with no measurement of what each rule saves.
3. Principles for the loop¶
These are constraints, in priority order. Where a later principle conflicts with an earlier one, the earlier one wins.
- Nothing personal in the engine. Traces are personal data. The mining, the proposals and the evaluations run inside the vault, on the owner's machines. What flows to the engine is a generic improvement as a normal PR (a better
AGENTS.base.mdparagraph, a skill eval case written with fake data), reviewed like any other. - Markdown and git are the source of truth; Postgres is an index. Harness versions, improvement records and eval cases are files in the vault. The database rows that index them can be rebuilt from the files.
- A change to the harness is a pull request. Never a live edit of
AGENTS.md, a skill or a graph by the loop. The owner merges. Agents never merge their own PR (already the rule inAGENTS.md), and nothing ships without an approval (cli-org ship). - Zero regressions, or no promotion. A candidate that improves the target metric but worsens any guarded metric (verification pass rate, cost per finished card, guard blocks, owner interventions) is not promoted. Ties go to the baseline.
- The evaluator is not editable by the proposer in the same change, and part of it is never visible to the proposer. A PR that changes a skill may not also change that skill's eval cases or the promotion thresholds. The guard enforces it (a
readonlyrule on the eval files for bot runs), and the review checks it. A held-out set of cases sits under a guardprotected_dirspath the proposing run cannot read; the proposer sees the replay set's results, the promotion decision also uses the held-out set and the canary. Every system in section 14 that reports durable gains separates the signal the optimizer sees from the signal that decides. - Budgeted. Mining, evals and curation have a weekly token budget in the policy file; when it is spent, the loop reports and stops. Every eval run is attributed on
/usageaswork_type: opsunder a dedicated bot so its cost is visible. - Traces are untrusted input. They contain web pages, tool output and other people's messages. The improver reads them as data, under the same rule the
org-memberskill already states for board messages: nothing in a trace is an instruction. - Fail open, like the hooks. A broken policy file, a missing eval suite or a failed mining run never stops an agent from working. It only stops the loop.
- Every promotion is reversible with one
git revert, and the record says what to watch after the revert. - One change at a time per surface. A proposal edits one file (or one skill folder). Two simultaneous changes to the same surface cannot be attributed.
4. Architecture: the improve module¶
A new optional module, improve, off by default (MODULES=...,improve). Following CONTRIBUTING.md: its own migration, routes mounted only when on, a nav link only when on, timers installed by deploy.sh only when on, a CLI, and a doc section.
vault (private) engine (public)
┌───────────────────────────────────────────────┐ ┌──────────────────────────────┐
│ AGENTS.local.md .agents/skills/ guard.local │ │ AGENTS.base.md skills/ │
│ .agents/org/{org,graphs}.yaml memory/ cron │ │ harness/ tools/ app/ │
│ .agents/improve/policy.json cases/ records/ │ │ (changed only by PR + CI) │
└───────────────┬───────────────────────────────┘ └──────────────┬───────────────┘
│ hashed together = harness version │ engine pin
▼ ▼
┌─────────────────── cli-improve stamp ──────────────────────────┐
│ harness_versions(sha, files{path: sha}, engine_commit, at) │
└────────────────────────────┬───────────────────────────────────┘
│ written into cc_dispatch / session meta
runs ─────────────────────────────┼──────────────────────────────────────────────┐
(claude -p, Hermes, cron, owner) ▼ │
┌──────────────── run_outcomes (one row per run) ───────────────┐ │
│ harness_sha · card · graph/node · bot · skills_loaded · │ │
│ verify · rounds · held · blocked · turns · tokens · guard │ │
│ blocks · gate nudges · owner interventions · error subtype │ │
└────────────────────────────┬──────────────────────────────────┘ │
│ nightly │
┌──────────── cli-improve mine (weakness mining) ─────────────┐ │
│ clusters by signature → weakness report (markdown) + │ │
│ one `improve` card per cluster, to the harness-engineer bot │ │
└────────────────────────────┬──────────────────────────────────┘ │
│ a card wake │
┌──────────── the harness-engineer bot (propose) ───────────────┐ │
│ reads redacted traces (cli-improve trace) → hypothesis → │ │
│ one diff to one surface → improvement record → branch │ │
└────────────────────────────┬──────────────────────────────────┘ │
│ │
┌──────────── cli-improve eval (probe) ────────────────────────┐ │
│ tier A static checks · tier B replay of recorded cases in a │ │
│ scratch vault + worktree · tier C behavioural contrast │ │
│ baseline vs candidate → eval_runs row + results in the record │ │
└────────────────────────────┬──────────────────────────────────┘ │
│ zero regressions │
┌──────────── PR + cli-org ship request (compose / promote) ───┐ │
│ owner merges → secondbrain-vault rules → new stamp → canary │◀──────────┘
│ window → auto `revert` card if guarded metrics regress │
└───────────────────────────────────────────────────────────────┘
│ weekly
┌──────────── outer loop: cli-improve report ───────────────────┐
│ proposals made / promoted / reverted, time-to-fix, escapes; │
│ proposes edits to policy.json through the same path │
└───────────────────────────────────────────────────────────────┘
4.1 Harness versions: the stamp¶
cli-improve stamp (also run by secondbrain-vault rules and by the dispatcher before each wake) hashes the set of files the policy names as surfaces and writes one row:
| Column | Holds |
|---|---|
sha |
sha256 over the sorted (path, file sha) pairs |
files |
jsonb of { "AGENTS.md": "…", ".agents/skills/ingest/SKILL.md": "…", ".agents/org/graphs.json": "…", ".agents/guard.local.json": "…", ".agents/improve/policy.json": "…", "cron:<profile>/<job_id>": "<prompt sha>" } |
engine_commit |
The .secondbrain pin |
first_seen_at, last_seen_at |
Default surface list (overridable in secondbrain.json → improve.surfaces): AGENTS.md, CLAUDE.md, .claude/settings.json, .agents/guard.local.json, .agents/skills/**/SKILL.md (following symlinks into the engine), .claude/agents/*.md, .agents/org/org.json, .agents/org/graphs.json, .agents/org/memory/*.md, .agents/improve/policy.json, and each Hermes cron job's prompt as read from cron_jobs.
The stamp travels with the run three ways, so every harness gets it:
- The dispatcher adds
harness_shato itscc_dispatchline;usage/runLog.tsalready parses that line and would gain one field. - For sessions without a dispatcher (the owner's interactive Claude Code, Hermes chats), the archive run looks up the stamp whose
first_seen_at ≤ session start < next first_seen_aton that host. ASessionStarthook that writes the sha into the transcript as ametaline is more exact and is proposed for Claude Code (harness/claude/settings.jsongains aSessionStartentry callingcli-improve stamp --print). - With the capture proxy on,
llm_exchanges.system_shaidentifies the exact system prompt, which is finer than the file hash and catches drift the files do not show (a Claude Code update changing its own preamble).
4.2 Run outcomes: one row per run, every label in one place¶
run_outcomes is a materialised join, rebuilt by cli-improve outcomes (idempotent, runs after usage-sync and agents-sync). One row per run (a usage_runs row, a Hermes session, or a cron execution):
| Group | Columns | Source |
|---|---|---|
| Identity | run_id, harness_sha, host, harness (claude-code, hermes), bot, team, work_type, started_at, ended_at |
usage_runs, archive_sessions, stamp |
| Task | card, graph, node, kind, project, feature, wake_reason, cron_job |
task_links, org_wakeups, cron_runs |
| Context | skills_loaded text[], rules_sha, resumed, session_runs |
Transcript (Skill tool uses and skill file reads), stamp, cc_dispatch |
| Cost | turns, input_tokens, cache_read, cache_write, output_tokens, duration_ms, tool_calls, tool_errors |
usage_calls, archive_messages steps |
| Outcome | finished (card completed in this run), verify_passed, verify_name, rounds_after (review rounds the card needed later), held, blocked_reason, error_subtype, is_error, status_posted (did the bot post a status, or did wake-done post the fallback) |
usage_runs, task_links.rounds, org_messages |
| Hooks | guard_blocks int, guard_reasons text[], gate_nudged bool |
A new agent-hooks.jsonl the hooks append to under the vault's .agents/improve/log/ (replacing the ~/.cache log), synced like usage |
| Owner | questions_asked, redo_requested, ship_denied, owner_rating |
org_messages kinds, org_approvals, a new ratings table (section 7) |
The guard and gate changes are small: today guard.py writes to ~/.cache/agent-guard.log; the proposal is one more line into the vault's .agents/improve/log/hooks-YYYY-MM-DD.jsonl with the run id from ORG_RUN when set. Both hooks stay stdlib-only and fail open.
4.3 Weakness mining¶
cli-improve mine [--since 7d] runs nightly. It computes, per harness version and per surface, the metrics in section 6 and looks for clusters:
| Signature | Example | Points at |
|---|---|---|
error_subtype × kind |
6 of 9 review cards this week ended error_max_turns |
The review node body or the reviewer's turn budget |
held × graph.node |
feature-build.test held 4 times |
The node's instructions, or the node needs splitting |
guard_reason × bot |
One bot hit the secrets rule 11 times | A skill that tells it to grep .env, or a rule it never read |
tool_errors × tool × skills_loaded |
cli-projects set failed in 70 % of runs that loaded company-report |
The skill's command table is stale |
rounds_after × assignee |
Cards built by one bot need 1.8 rounds on average, others 0.4 | The bot's memory file or its role text |
tokens / finished × cron_job |
One cron job costs 40 % of the week's tokens and its outputs are never rated up | The cron prompt, or the job's schedule |
verify_passed = false × verify_name |
The same check fails after every deploy card | The deploy skill or the check itself |
status_posted = false |
Runs that end without a report | The wake prompt's closing instruction |
Each cluster above a threshold (policy: minRuns, minLift) becomes:
- A section in
.agents/improve/reports/YYYY-MM-DD.md(markdown, in the house research style, so it reads like any other note and is searchable). - One kanban card, kind
improve, assigned to the harness-engineer bot (a role in the vault'sorg.yaml; the engine only requires thereviewcapability and adispatch-free team), with the card body generated from a template that carries the contract (section 5.3): the cluster, the runs, the surface suspected, the metric to move, the budget.
Mining is deterministic SQL over run_outcomes, no model call. That keeps it cheap, reproducible and immune to the traces' content.
4.4 Propose: the harness-engineer bot¶
The bot works an improve card like any other card, under the org-member skill plus a new improve skill. The procedure:
cli-improve trace <run_id> [--steps tool_call,tool_result] [--max-chars 20000]: a redacted, compacted view of the run (the redactor inarchive/redact.tsalready exists), so the bot does not read raw transcripts. Thencli-improve records --surface <path>: every earlier record for the surface, promoted or not. The records folder is the loop's experience library; a hypothesis already tried and not promoted is not tried again without new evidence.- Form one hypothesis naming one surface and one mechanism ("the
company-reportskill'sverifyrow omits--kind, so the bot retries the command three times"). - Make the change in a worktree, never in the live file (the
readonlyguard rule for bot runs coversAGENTS.md,.agents/skills/,.agents/org/,.agents/improve/cases/and.agents/improve/policy.json; the worktree path is exempt). - Write
.agents/improve/records/YYYY-MM-DD_<slug>.md: the record (section 8). - Run
cli-improve eval --record <path>(section 4.5). Attach the results to the record. - If tiers A and B pass with zero regressions: push the branch, open the PR,
cli-org ship request <card> "<summary>" --pr <url>, post a status, complete the card. Otherwise: record the negative result (negative results are the cheapest thing the loop produces and prevent re-proposing), block the card with the reason, and stop.
Budget per card: policy.proposeBudget (default 150 000 tokens, 30 turns). The dispatcher enforces it as it enforces any card budget (section 5.3).
For generic findings (a fix to AGENTS.base.md or a shared skill), the bot writes the same record and opens the PR against the engine from a worktree of .secondbrain, as AGENTS.md already instructs, with fake data in any new eval case and scripts/check-public.sh run first.
4.5 Probe: cli-improve eval¶
Three tiers, cheapest first, each a gate for the next.
Tier A: static checks (seconds, no model):
| Check | On |
|---|---|
secondbrain-vault rules --check compiles; AGENTS.md under the policy's size cap (tokens, counted with the same tokenizer the ledger uses) |
Rules |
Frontmatter has name and description; description under 1 024 characters; every command the skill names resolves on PATH or in the engine; every [[wikilink]] or path it names exists; no personal values (the leak check) |
Skills |
graphs.json validates: known assignees or role: specs, acyclic after, every node's kind has a capability the assignee holds (nodeNeed in org.ts) |
Graphs |
guard.local.json parses; python3 -m pytest harness passes against the candidate rules; the readonly rules still cover the eval files |
Guard |
| Prompt under the job's token cap; delivery target set | Cron |
policy.json validates against its schema; thresholds within allowed ranges |
The improver itself |
Tier B: replay of recorded cases (minutes to an hour, budgeted):
A case is a recorded task with a check. Cases come from history, which is the point of having the ledger:
- A card whose run produced a
verify_passedrow, with the card body, the project state before the run (a git commit of the vault or the repo) and the verification command. - A cron job output the owner rated up, with its prompt and the inputs available at that time.
- A fact-check table with at least one
incorrectrow: the output and its sources. - A hand-written case in
.agents/improve/cases/<slug>/(case.json: setup, prompt, check, budget; fake data only if it ever goes to the engine).
Cases are split into a replay set the proposer may read and a held-out set under .agents/improve/cases-heldout/, which the policy lists in guard.local.json protected_dirs for bot runs (the guard blocks any command or file tool naming the path). Held-out cases are drawn from the same history by mine, rotated monthly, and their results are shown to the proposer only after the promotion decision.
cli-improve eval makes a scratch vault (secondbrain-vault init into a temp dir, the case's fixture files copied in, the candidate harness files overlaid), runs the case under baseline and candidate with the same model and the same budget, and records both trajectories as eval_runs rows that /usage shows like any other run (host eval, bot harness-engineer). The suite is policy.replay.cases (default: the 20 most recent passing cases plus every case tagged regression), capped by policy.replay.budgetTokens.
Runs are not deterministic, so each case runs policy.replay.repeats times (default 2) and the comparison uses pass counts, not single runs.
Tier C: behavioural contrast (no ground truth needed):
For every case, baseline and candidate trajectories are diffed on the metrics in section 6: tool calls, tool errors, turns, tokens, guard blocks, whether a status was posted, whether the artifact named in the contract exists. For open-ended outputs (a research note, a stand-up) a pairwise judge is used with a rubric file (.agents/improve/rubrics/<kind>.md) and position swapping; its preference is reported, never used alone to promote.
Promotion rule (policy, defaults):
- Tier A: all pass.
- Tier B: candidate pass count ≥ baseline on every replay case and every held-out case; strictly greater on at least one, or equal with cost (tokens per pass) down by ≥ 10 %.
- Tier C: no guarded metric worse by more than the noise band (policy
noiseBand, default 5 % relative, estimated from baseline-vs-baseline repeats). - Total eval cost within the card's budget.
Anything else is "not promoted", recorded with the numbers.
4.6 Compose and promote¶
- The PR merges (owner). For the vault:
secondbrain-vault rules,cli-improve stampruns, a new harness version exists. cli-improve promote <record>writes theimprovementsrow (section 8) withpromoted_atand the sha pair, and opens a canary window (policy.canaryDays, default 7).- During the window,
minecompares the guarded metrics of the new sha against the previous sha on live runs. If any guarded metric regresses beyond the noise band with at leastpolicy.minRunsruns, it creates arevertcard (kindimprove, priority high) with the exactgit revertand the numbers. A person decides; the loop never reverts on its own. - After the window the record is marked
confirmedorregressed.
4.7 The outer loop¶
cli-improve report runs weekly and writes .agents/improve/reports/weekly-YYYY-Www.md:
| Metric of the improver | Why |
|---|---|
| Proposals made, evaluated, promoted, confirmed, reverted | Yield |
| Median time from first cluster to promotion | Speed |
| Regressions that the canary caught but the eval did not ("escapes") | The eval suite is too thin where escapes happen |
| Eval tokens per promotion | Cost |
| Clusters re-proposed after a negative result | The mining signature is too coarse |
When yield is low or escapes are high, the report ends with a proposal to policy.json: a new signature, a tightened threshold, a case added to the regression set, a bigger repeats. That proposal goes through exactly the same propose → eval → PR path, and its eval is Tier A plus a dry run of mine on the last four weeks showing what the new policy would have flagged. This is the bounded recursion: the improver edits how it improves, through the same gate.
5. Each surface, in detail¶
5.1 The org config files: roster and graphs¶
Today: org.yaml and graphs/*.yaml are compiled by a script in the vault into org.json and graphs.json; the engine imports one and reads the other.
Proposal:
- Move the compiler into the engine:
secondbrain-vault org compile(Python, stdlib, intools/sbvault.py), with a schema the engine owns and tests (tools/test_sbvault.py). The template ships an exampleorg.yamland one graph with fake names. The roster and graphs become engine-validated surfaces:graph apply --dry-runalready plans a graph; the compiler adds--check. - Add contract fields to a graph node, all optional with defaults from the template:
nodes:
- id: build
assign: role:engineer
kind: build
title: "Build {feature}"
objective: "Implement the feature as designed in {design_doc}."
returns: ["a PR URL", "the test command and its output"]
budget: { turns: 40, tokens: 400000 }
after: []
- id: verify
assign: role:qa
kind: review
returns: ["a verification recorded with cli-projects verify"]
budget: { turns: 15, tokens: 120000 }
after: [build]
applyGraph fills them into the card body under fixed headings so the dispatcher and the reviewer can parse them.
- What the loop may propose on a graph: add a node (a verifier before review, a "read the design doc" node before build), remove a node whose cards never change the outcome (same pass rate without it, fewer tokens), reassign a node (role:qa instead of lead), change an edge, change a budget. Each is one diff to one yaml file.
- How it is evaluated: graph-level metrics from run_outcomes joined on task_links.graph: features finished per graph application, total tokens per finished feature, rounds, held nodes, wall time from first card to last. Tier B replays a whole graph on a recorded feature in the scratch vault only when the budget allows (it is the most expensive case type); Tier A plus live canary is the usual path for graph edits.
- Roster proposals are restricted to text and capability fields (role, expertise, capabilities, reports_to), never to adding merge or deploy to a bot; those two stay owner-only by policy and the eval refuses the diff.
5.2 The skills layer¶
Today: five shared skills and whatever the vault adds, none with a test. A skill's quality is only visible when someone notices an agent misusing it.
Proposal:
- Every skill folder may have
evals/:cases.jsonl(one case per line: a prompt or card body, fixture paths, the check, optionalexpect_trigger: true|false) andrubric.mdfor open-ended outputs. The template shows one case per shared skill, with fake data. The engine's CI runs Tier A onskills/(frontmatter, commands resolve, no leaks); Tier B runs only in vaults, because it costs model calls. - Two eval kinds, as the skill-creator practice distinguishes: trigger accuracy (given 20 prompts, does the agent load this skill on the ones that should and not on the ones that should not: the description is the surface) and task quality (given the skill, does the agent finish the case within budget with the check passing).
- Skill versions: a
version:field in frontmatter is not needed; the file sha in the harness version is the version. The record names the sha pair. - What the loop may propose: a tighter description (trigger failures), a corrected command table (tool errors), an added "when this fails, do X" paragraph (a recovery that worked in three traces), a split of a long skill into two, removal of a paragraph nobody's trajectory ever used (dead instruction text is paid for on every turn). Deletions are proposals too and need the same zero-regression eval.
- Skill compilation (section 7.4): when mining finds the same multi-step recovery in several successful traces that no skill describes, it files an
improvecard whose expected output is a new private skill with an eval case, not an edit. - Private over shared: a vault's
.agents/skills/<name>/already wins over the engine's symlink. A proposal that improves a shared skill lands first as a private override in the vault (fast, personal data allowed), and only becomes an engine PR once it has run a canary window and been rewritten with fake data.
5.3 The kanban board¶
Today: the kanban is the board module in the engine — migration, board_* SECURITY DEFINER functions, three auth doors, cli-board, the page, a legacy importer for old Hermes exports (the projection was removed after the 2026-10-26 cutover), and zen-agent's board backend and lane loop (built; see board-design.md for the full design and the staged rollout in its section 8). Stage 0 (mirror, read-only) is what's running: the engine's cards mirror Hermes's own kanban while the Hermes dispatcher still claims and runs cards in its own SQLite. Cards still carry prose bodies — the contract fields below are not yet filled in by task create — and the dispatcher still wakes a bot with a prompt that says "follow the org-member skill".
Proposal (on top of the built board module):
- A card contract, written into the body under fixed headings by
cli-org task create,graph applyandredo, parsed by the dispatcher and bycli-improve:
## Objective
One paragraph: what done means.
## Returns
- a path, a PR URL, a verification, a message id
## Budget
turns: 30 · tokens: 300000 · wakes: 3
## Scope
Folders, repos, tools in scope. Everything else is out.
## Verification
The command or check and its expected result.
This is the AGENTS.base.md completion contract (Outcome, Verification, Constraints, Boundaries, Stop when) made machine-readable. The dispatcher passes --max-turns from the budget and stops the run at the token cap; wake-done records whether the returns exist. A card without a contract gets the policy's defaults and is flagged in the weekly report.
- Card kinds gain improve and revert. The board shows them in their own swimlane on /agents so the owner sees the loop's work apart from the company's.
- Outcome labels on the card: task_links gains finished_at, wakes, tokens, result (done, blocked, held, escalated), filled by wake-done. These are the per-card labels run_outcomes aggregates.
- Held and blocked reasons become structured: block <id> "<why>" keeps the free text but the dispatcher's held: status and the review redo carry a reason code (budget, missing-input, tool-error, needs-owner, review-changes) that mining can cluster.
- What the loop may propose about the board: default budgets per kind, the wake prompt text (once the dispatcher's prompt is a file the stamp covers), the review-round limit, the backoff settings. All become policy.json or env values with a record, not magic numbers.
- The board module's own rollout is the remaining work, not an open question. board-design.md section 8 stages it: stage 0 (mirror, read-only, done) → stage 1 (engine writes, Hermes still runs) → stage 2 (record authoritative; task_links becomes a view) → stage 3 (runtime optional: zen-agent lanes carry real work, the hub's board routes and the old task_links table are deleted). The cards table, the functions and RLS already exist; what's left is pointing cli-org task create, graph apply and the dispatchers at it instead of at Hermes, and judging stage 3 by this proposal's metrics (10.4).
5.4 The project and org structure¶
Today: PARA folders; the projects module with stages and verifications; dev logs per project with a Resume block; the org with teams, leads and channels; the fund, social and other modules as consumers.
Proposal:
- Verifications are the ground truth of the loop.
cli-projects verifyrows are the clearest outcome label the system has. The proposal addsrun_idto a verification (the dispatcher setsORG_RUN;cli-projectsreads it) so a passing check is attributable to the run and the harness version that produced it. No schema change to the vault. - Dev logs stay the human trace. The loop writes its own records in the same shape (section 8) and the
devlog_gatecontinues to require one for any change to.agents/. Improvement records satisfy the gate because they live under a dev-log root (.agents/improve/records/is added to the default roots). - The Resume block is the inter-session memory contract, and mining checks it: a project whose Resume block has not changed in 30 days while cards for it finished is a signal that bots skip the handoff. That becomes a cluster ("Resume block stale") pointing at the
org-memberskill's step 5 or the dev-log gate. - Division and team metrics: per team, tokens per finished card, rounds, held rate and owner questions per week, shown on
/orgnext to the stand-ups. Leads see their own numbers in the daily routine (org-memberstep 10 gains "readcli-improve team-report"), which is the macro level of the macro/micro split: a lead's delegation choices are judged on team outcomes, a member's execution on card outcomes. - Owner interventions as a first-class metric. Questions for the owner,
redo,ship deny, and the new ratings are the cost the owner pays; the loop's job is to reduce them without reducing verifications.
5.5 Cron jobs¶
Today: Hermes cron jobs with prompts, synced into cron_jobs, runs and outputs shown on /cron; server timers for modules; nothing grades an output.
Proposal:
- Prompts become stamped surfaces.
cron_jobs.promptis hashed into the harness version (one entry per job), so an output is attributable to a prompt version. - Rate an output. A
ratingstable (kind,ref,rating∈ {up, down, skip},note,by,at) with two write paths: a button on/cronand/org, and a Telegram reply the notifier already supports for ship approvals ("rateup|down "). The owner's thumbs are the only cheap label a daily briefing can have. - Cron hygiene mining: tokens per job per week against rating rate; jobs whose outputs are never opened (the
/cronpage can record a view) or alwaysskip; jobs whose prompt duplicates another's; jobs that fail on schedule. Proposals: rewrite the prompt, merge two jobs, retire a job, reschedule to when its inputs exist. Retiring is a proposal with a canary (the owner can rate the absence). - Fact-check as a cron grader. For cron jobs that produce claims (a briefing, a report), the
fact-checkskill's table is stored (fact_checkstable: output ref, claims, verified, incorrect, unresolved) and its incorrect rate is a guarded metric for that job's prompt. - Claude-side routines: the
orgmodule already hasroutinewakeups. The proposal gives them a file in the vault (.agents/org/routines.yaml: name, schedule, bot, prompt, budget) compiled with the roster, so Claude Code routines are versioned and graded exactly like Hermes cron. Today they live wherever the dispatcher keeps them. - The engine's own timers (
map-build,metrics,usage-sync, …) are not model-driven and stay out of the loop, but/cronshows their last status already and mining counts a failing timer as an ops cluster.
5.6 The engine code¶
The engine is the one surface with a complete evaluation already: the Vitest suite against a real Postgres, the Python suite, the build, the leak check, CI on every PR. The loop does not need to invent anything here; it needs to use it.
- Engine proposals from the loop are ordinary PRs: branch, tests first,
check-public.sh, the PR body saying what ran. The harness-engineer bot has thecodecapability for the engine repo only when the owner gives it (--repo), asbotRepos()already enforces. - Two additions make the engine a better subject of the loop: a
test:skillsCI step running Tier A overskills/andvault-template/, and aharness/evals/folder with the guard's and gate's own replay cases (payloads that must block, payloads that must pass) so the hooks' behaviour on real-world call shapes is a regression suite, not only unit tests. - Engine changes are promoted by the merge; the canary is each vault moving its pin, which already happens by hand. The weekly report lists engine PRs by the loop and their state.
6. Evaluation design¶
6.1 Metrics¶
| Metric | Definition | Role | Direction |
|---|---|---|---|
| Verification pass rate | Finished cards whose feature has a passing verification recorded by the run, over finished cards | Target | ↑ |
| First-wake finish rate | Cards finished in one wake, over cards started | Target | ↑ |
| Rounds per reviewed card | task_links.rounds at completion |
Target | ↓ |
| Held rate | Cards that hit the wake cap, over cards started | Guarded | ↓ |
| Tokens per finished card | Sum of all runs' tokens for the card, over finished cards; cache reads weighted at their price ratio | Guarded | ↓ |
| Turns per run | Guarded | ↓ | |
| Tool error rate | tool_result steps marked error, over tool calls |
Guarded | ↓ |
| Guard blocks per run | Guarded | ↓ | |
| Status posted rate | Runs that posted a status with their run id, over runs |
Guarded | ↑ |
| Owner interventions per week | Questions for the owner + redo + ship denies + down ratings | Guarded | ↓ |
| Fact-check incorrect rate | Incorrect claims over checked claims, per output kind | Guarded | ↓ |
| Memory hit rate | Runs where a recalled fact or memory line appears in the trajectory, over runs that called recall |
Diagnostic | ↑ |
| Repeated-mistake rate | Clusters re-appearing after a promoted fix for them | Diagnostic (outer loop) | ↓ |
| Improver yield, escapes, eval cost per promotion | Section 4.7 | Outer loop |
A "target" metric is the one a proposal says it will move. "Guarded" metrics may not get worse. Every metric is computed per harness version and per surface-of-interest (bot, graph node, skill, cron job).
6.2 Why behavioural contrast, and its limits¶
Ground truth is scarce: most of what the agents produce (a note, a stand-up, a design) has no answer key. What we can always do is run the same recorded task under two harnesses and compare the trajectories. Fewer tool errors, fewer turns, the contract's artifact present, the verification passing: these are observable without a judge. The literature's "behavioural contrast" is this.
Limits, and what the design does about them:
| Limit | Mitigation |
|---|---|
| Runs are stochastic | Repeats per case; compare pass counts and medians; estimate the noise band from baseline-vs-baseline |
| Small samples | minRuns before any cluster or canary verdict; the weekly report shows confidence as a range, not a point; the loop prefers a longer canary to a bigger eval |
| Recorded cases drift from reality (the repo moved on, the API changed) | Cases carry a fixture commit; a case that fails under baseline is marked stale and dropped from the gate, listed in the report |
| Teaching to the test | The replay set is the recent 20 plus tagged regressions, rotating; the held-out set is unreadable to the proposer; the canary on live runs is the real test |
| The judge's bias | Pairwise, position-swapped, rubric-driven, reported but never sufficient for promotion |
| The proposer games the eval | Principle 5: eval files are read-only to the proposing run; a PR touching both a surface and its eval is refused by the policy check |
| Cost | A weekly budget; Tier A before B before C; graph replays only when budget allows |
6.3 What a regression is¶
A guarded metric worse than baseline by more than the noise band, on the replay set or in the canary window, with at least minRuns runs. A single bad run is never a regression. A new guard block pattern that did not exist before is a regression regardless of count, because it means the change made an agent try something the rules forbid.
6.4 Evaluating the improver¶
The outer loop's own metrics (section 4.7) are the evaluation of policy.json. A policy change is promoted when a dry run over the last four weeks would have produced at least as many later-confirmed proposals and no more escapes. Since this is backtesting, the canary window for a policy change is longer (default 21 days).
7. Memory¶
Memory is where the loop's gains accumulate between runs, so it deserves its own design. Today there are five memories with no relationship: the fact table, the per-bot files, the archive, the skills and the Resume blocks.
7.1 Layers and their contract¶
| Layer | Holds | Written by | Read by | Lifetime |
|---|---|---|---|---|
| Episodic: the archive and ledger | What happened, verbatim | Collectors | cli-improve trace, the /usage session view, mining |
Forever (it is the index of the transcripts) |
Semantic: memory_facts |
Sourced claims, confirmed or inferred, superseded when wrong | Any agent, cli-org remember |
cli-org recall |
Until retired |
| Procedural: skills | How to do a kind of task | PR | Agents, by trigger | Until replaced |
Working: the bot's playbook .agents/org/memory/<bot>.md |
Lessons for this role, short | The bot (reflection), the curator | recall, and read in full at wake |
Curated |
Persona (built, agent-memory.md): org_agent_episodes, org_agent_memory, the generated .agents/org/memory/<bot>/core.md |
One row per finished wake; first-person lessons with counts; the active rows under a budget | cli-org wake-done (mechanical) and its reflect step (one cheap-model call); the owner or a lead corrects a line |
The wake prompt (core block), recall, /org/agents/<name> |
Closed, never deleted |
| Handoff: the Resume block | Where a project stands | The agent finishing a session | The next agent | Overwritten each session |
The rule that connects them: a lesson moves up when it recurs. A recovery seen in three episodes becomes a playbook bullet; a bullet marked helpful ten times with no harmful mark becomes a proposal for a skill paragraph or an AGENTS.local.md rule; a fact cited by several runs is promoted to a confirmed fact with the runs as sources. Each promotion is a proposal with a record, not an automatic write.
7.2 Playbooks instead of flat memory files¶
The flat memory file has a known failure: an agent rewriting it wholesale compresses away detail and loses lessons (the literature calls this context collapse). The proposal gives memory/<bot>.md a fixed structure of itemised bullets with ids and counters, and only ever appends or edits one bullet at a time:
# atlas: playbook
## Delegation
- [m-014 · +7 −0 · 2026-10-02] A review card needs the PR URL in its body, or the reviewer spends its first ten turns finding it. Source: t_3f9a, t_41c0.
## Tools
- [m-021 · +3 −1 · 2026-10-15] `cli-projects verify` wants `--kind test|deploy|manual`; without it the command fails and the bot retries. Source: run org-atlas-20261015-061200.
+7 −0 are helpful and harmful marks: the bot adds +1 when a bullet it read changed what it did and the card finished; the curator adds −1 when a bullet was read in a run that failed in the way the bullet addresses. cli-org recall ranks bullets by score and recency; bullets at −3 or 90 days unused are proposed for retirement in the weekly report. The structure is enforced by Tier A (a bullet without id, counters and source fails the check), and an edit that removes more than one bullet is refused for a bot run.
7.3 Facts that are measured¶
memory_facts gains recalled_count, last_recalled_at, and cited_in_runs text[] (filled when a run's trajectory quotes the fact's claim or id). A fact_checks table stores the fact-check skill's tables, and a fact contradicted by a fact-check is flagged for forget with the check as source. The curator's weekly pass: merge duplicates (same subject, same claim modulo whitespace), propose supersession for conflicting facts (both shown to the owner), and promote facts cited in five or more runs to the playbook of the bots that cited them.
Recall itself gets two improvements the engine can ship today: the fact table gets an embedding column (the vault's embeddings provider already exists) so recall is hybrid like /search, and recall gains --sources facts,memory,vault,devlog,records so improvement records and dev logs are searchable by the bots.
7.4 Skill compilation¶
Mining has one signature for success too: a sequence of tool calls (normalised: tool name, command head, argument shape) that appears in ≥ 3 finished runs across ≥ 2 bots, is ≥ 4 steps long, and matches no existing skill's command table. That is a procedure nobody wrote down. The improve card for it asks the harness-engineer bot to write the skill (name, description with trigger phrases, the steps, the failure modes seen in the traces, one eval case) as a private skill in the vault. If the skill then shows a Tier B improvement on cases that previously lacked it, it is promoted; after a canary it may be proposed to the engine with fake data.
7.5 Compaction as a measured surface¶
For headless runs, what enters the context is the biggest cost lever (AGENTS.base.md's "Context budget" section exists because of it). Today the levers are ORG_SESSION_MAX_RUNS and output caps. The proposal makes the wake prompt assembly explicit and measurable: the dispatcher (or the runtime in section 10) builds the wake context from named parts (rules, card, playbook, recall(card title), thread, resume block), logs the token count of each part in cc_dispatch, and the policy sets each part's cap. Mining then answers "did runs with the playbook part finish more often" with real numbers, and a proposal can be "drop the thread part for review cards, it never changes the outcome".
8. Tracing the changes: the improvement record¶
Every proposal, promoted or not, is one markdown file and one row.
The file .agents/improve/records/YYYY-MM-DD_<slug>.md:
# Record: company-report verify row omits --kind
### Surface: .agents/skills/company-report/SKILL.md
**Opened:** 2026-10-22 · **Card:** t_8b2e · **By:** harness-engineer (claude-code@server)
**Cluster:** tool_errors × cli-projects × company-report, 9 runs, 2026-10-15 to 2026-10-21
**Hypothesis:** The skill's table shows `verify --pass --kind test ...` once but the "At the end of a build session" list omits `--kind`; bots copy the list. Expected: tool error rate on `cli-projects verify` from 70 % to under 10 %, turns per build card −2.
**Change:** one line added to step 2 of "At the end of a build session". Diff: <PR url>
**Harness:** 3f1c…(baseline) → 9a07…(candidate)
## Evaluation
| Tier | Result |
|---|---|
| A | pass |
| B | 6 cases × 2 repeats: baseline 9/12 pass, candidate 12/12; tokens per pass −18 % |
| C | tool errors 14 → 1; turns median 22 → 19; guard blocks 0 → 0; status posted 12/12 both |
## Decision
Promoted 2026-10-23 (merge <PR url>). Canary to 2026-10-30.
## Canary
2026-10-30: 31 live runs. Tool error rate 4 %. Confirmed.
## Watch after revert
If reverted: expect `cli-projects verify` errors to return within a week on build cards.
Records are also the library the proposer reads before proposing (section 4.4), which is the role the Harness² paper gives its per-harness experience notes: a situation, the lesson, the evidence. A record with state: not-promoted is as valuable as a promoted one.
The row in improvements: id, record_path, surface, surface_path, cluster_signature, baseline_sha, candidate_sha, pr_url, card, state (proposed, not-promoted, promoted, confirmed, regressed, reverted), eval jsonb, opened_at, promoted_at, closed_at. The row is an index of the file; cli-improve outcomes rebuilds it from the files.
The page /improve (module on): the list of records by state; one record with its before/after metrics drawn from run_outcomes; the harness version timeline (each sha, which files changed, which records); the weekly report. Everything read-only; the writes happen through git and the CLI, as everywhere else in the app.
Dev logs: a promotion also appends one line to the project's dev-log README Resume block ("Decisions: skill X changed per record Y") when the record names a project. The gate is satisfied by the record itself.
9. Settings and files this adds¶
All optional, all with defaults that work for a stranger, per CONTRIBUTING.md.
| Where | Name | Default | Purpose |
|---|---|---|---|
| env | MODULES gains improve |
off | Mounts /api/improve, the /improve page, installs the two timers |
| env | IMPROVE_WEEKLY_BUDGET_TOKENS |
2000000 |
Hard cap on mining, eval and curation tokens per ISO week |
| env | IMPROVE_CANARY_DAYS |
7 |
Canary window after a promotion |
| env | IMPROVE_MIN_RUNS |
20 |
Runs before a cluster or canary verdict counts |
secondbrain.json |
improve.surfaces |
the list in 4.1 | Files hashed into the harness version |
secondbrain.json |
improve.bot |
harness-engineer |
The roster name that gets improve cards |
secondbrain.json |
improve.exclude |
["CURRENT/", "Areas/Finance/"] (same as recall) |
Vault paths cli-improve trace never shows |
| vault | .agents/improve/policy.json |
shipped by the template | Signatures and thresholds, replay set, repeats, noise band, budgets, canary days |
| vault | .agents/improve/cases/, rubrics/, records/, reports/, log/ |
empty | Cases, judge rubrics, records, reports, the hooks' log |
| vault | .agents/org/routines.yaml |
example | Claude-side routines (5.5) |
| engine | skills/*/evals/ |
one case each | Tier A in CI |
| engine | harness/evals/ |
the hooks' replay payloads | Tier A in CI |
| timers | secondbrain-improve-mine.timer (daily 04:30 UTC), secondbrain-improve-report.timer (weekly, Monday 05:30 UTC) |
installed with the module | |
| CLI | cli-improve stamp | outcomes | mine | trace | eval | record | promote | report | team-report |
||
| migration | harness_versions, run_outcomes, improvements, eval_runs, ratings, fact_checks; columns on task_links, memory_facts, verifications |
Forward-only, as always |
Guard additions (built-in, every vault, because nobody wants the alternative): a bot run (ORG_RUN set) may not write to .agents/improve/policy.json, .agents/improve/cases/, or any evals/ folder. Owner sessions are unaffected.
10. A harness of our own?¶
The owner asked whether a runtime of our own, with a minimal agent core such as the pi packages (the repo formerly known as pi-mono) at its centre, would cut bloat and complexity. The honest answer is: for one of the three ways agents run here, probably yes; for the other two, no.
10.1 Three ways agents run today¶
| Mode | Runtime | Who drives | What hurts |
|---|---|---|---|
| Interactive | Claude Code in the vault | The owner | Little. The hooks, skills and subagents work; the ledger records it |
| Headless bot wakes | claude -p under the dispatcher, resumed up to ORG_SESSION_MAX_RUNS times |
The board | Every turn re-sends the whole conversation; the system prompt and tool list are Claude Code's, not ours; compaction is opaque; the trace needs a proxy to be complete; the dispatcher, the wake prompt and the budget live outside the engine |
| Cron and chat | Hermes | The clock, Telegram | Hermes owns its loop; the engine syncs its outputs |
The headless mode is where the loop's levers (context assembly, budgets, tool contracts, traces) are weakest, and it is also the mode with the highest token bill. It is the right place for a pilot.
10.2 What a minimal runtime would give the loop¶
- Context assembly as code. The wake context is built from named parts with caps (section 7.5), so every part is a measured surface and compaction is a policy, not a surprise.
- In-process hooks. The guard and the dev-log gate run as functions on
tool_callandsession_endevents instead of subprocesses parsing JSON, with the same rule files; the Python hooks stay for Claude Code and Hermes, and share the test payloads inharness/evals/. - Traces for free. A session file per run, with every message and tool call, in a format the archive collector reads directly; no capture proxy needed for the system prompt.
- Tools as contracts.
cli-org,cli-projects,cli-improvebecome typed tools with schemas the eval can lint and the model sees exactly, instead of shell commands described in a skill. - Budgets enforced where they are counted. Turn and token caps from the card contract stop the loop in the runtime.
- Subagents with contracts. A reviewer or a verifier spawned with an objective, a return schema and a budget, which is the contract-based interface the research describes and which today exists only as a card body.
- Provider freedom. Headless work can use whichever model the policy names, and the eval can run a case under two models as easily as under two prompts.
10.3 What it would cost¶
- A new thing to maintain, in TypeScript, in the engine (
app/server/src/agent/or a sibling package), with tests, against providers that change. - Feature loss against Claude Code: its tool set, its subagent and skill loading, its permission model, its session resume and compaction, its first-party support for the Claude subscription. Some of this the runtime must re-implement; some it must do without.
- Two harnesses to keep in parity for the shared rules and hooks (three, counting Hermes). The stamp and the shared eval payloads are what make parity testable.
- Risk that the pilot becomes the project. The phase plan bounds it: the pilot is judged by the same metrics as any other proposal.
10.4 Recommendation¶
Pilot a thin headless runtime for bot wakes only, built on a minimal, well-maintained agent core rather than written from scratch, behind a per-bot switch in the roster (runtime: zen-agent next to claude-code and hermes, which org_agents.runtime already models). Keep Claude Code for the owner and Hermes for cron. Judge the pilot at the end of phase 5 on tokens per finished card, first-wake finish rate and trace completeness against the claude -p bots on the same graph nodes. The concrete candidate cores and their trade-offs are in section 14, which also records what could and could not be verified about them.
Status (2026-10-25): the pilot runtime exists as zen-agent/ (described in zen-agent.md), a standalone package on @earendil-works/pi-agent-core: personas from the roster, skills, any provider, the board and the retrieval API as typed tools, the guard in process, budgets in the loop, context assembled from capped named parts, compaction as policy and one JSONL trace per run. It already has a board backend and a lane loop against the engine's board module (board-design.md section 7). What is not yet done: the runtime: zen-agent switch in the roster schema and the dispatcher calling zen-agent wake, and the archive collector reading its traces — so bots still run under claude -p today, and the pilot's comparison in 10.4 has not been run yet. The plan for the dispatcher, the ledger import, rules and a classifier in front of every wake and a cheaper default model is zen-runtime.md.
11. Phases¶
Each phase has a completion contract in the AGENTS.base.md sense. No phase depends on the pilot in section 10.
| Phase | Delivers | Done when |
|---|---|---|
| 0. Stamp and outcomes | improve module skeleton; harness_versions, run_outcomes; cli-improve stamp | outcomes; cc_dispatch.harness_sha; SessionStart stamp hook; hooks log into the vault; /improve page read-only with the version timeline |
Every run of the last 7 days on /usage shows a harness sha; run_outcomes has one row per run; npm test covers the join |
| 1. Mining and reports | cli-improve mine, the signatures in 4.3, the weekly report, improve cards to a roster bot; ratings table and the /cron rate button; fact_checks table |
A week of nightly reports exists; at least one cluster became a card a person acted on; no model tokens spent by mining |
| 2. Static checks and skill evals | Tier A for rules, skills, graphs, guard, cron, policy; skills/*/evals/ with one case each; harness/evals/; CI step test:skills; the org and graph compiler moved into secondbrain-vault |
CI runs Tier A on the engine; cli-improve eval --tier A passes on the template vault and fails on a planted bad skill |
| 3. Replay and contrast | Tier B scratch-vault replay, Tier C contrast, eval_runs, the noise-band estimate, the promotion rule, cli-improve record | promote, canary and revert cards |
One recorded proposal (hand-written) goes through eval, PR, promote and canary with its numbers on /improve |
| 4. The harness-engineer bot | The improve skill, cli-improve trace, the readonly guard rules, card contracts on the board, budgets enforced by the dispatcher |
Three bot-made proposals evaluated; at least one promoted and confirmed; zero escapes; eval cost under budget |
| 5. Memory | Playbook format and counters, measured facts, hybrid recall with more sources, the curator pass, skill compilation signature | Memory hit rate and repeated-mistake rate on the report; one compiled skill promoted |
| 6. Outer loop | policy.json proposals from the weekly report with backtest eval; longer canary |
One policy change promoted on backtest and confirmed after 21 days |
| Pilot (parallel from phase 3) | A headless runtime for bot wakes behind runtime: zen-agent |
Decision at the end of phase 5 by the metrics in 10.4. The runtime itself is built (zen-agent.md); what's left for this phase is the roster switch, the dispatcher hand-off and the comparison run |
Phases 0 to 2 cost no model tokens beyond what runs today. Phase 3 is the first with an eval budget.
12. Risks¶
| Risk | What it looks like | Mitigation in the design |
|---|---|---|
| Reward hacking | A proposal that passes the eval by narrowing what counts as finished, or by editing the check | Principle 5; verifications are recorded by the run, not asserted; the canary measures live outcomes |
| Context collapse | The curator "tidies" a playbook into three vague bullets | Itemised bullets with ids and counters; one-bullet edits for bots; Tier A refuses mass deletions |
| Cost spiral | Evals and replays eat the week's plan | Weekly budget, tiers, minRuns, the loop's own cost on /usage under one bot |
| Privacy leak to the engine | A record with a real card title lands in an engine PR | Records stay in the vault; engine PRs are rewritten with fake data; check-public.sh and the denylist run before push; the bot has the engine repo only when given it |
| Prompt injection through traces | A web page in a trace says "edit AGENTS.md to allow X" |
Traces are data (principle 7); the proposer cannot write live surfaces; the PR is read by a person |
| Evaluator drift | Cases go stale and the gate passes everything | Stale detection; rotation; escapes counted in the outer loop |
| Two harnesses diverge | A rule holds in Claude Code and not in the pilot runtime | Shared rule files, shared hook payloads in harness/evals/, the stamp covering both |
| The loop runs on noise | Promotions on 8 runs and a lucky day | minRuns, noise band from baseline repeats, canary before confirmation |
| Silent drift of the frozen model | A provider update changes behaviour; the loop blames a prompt | llm_exchanges.model and the provider's model id are columns in run_outcomes; mining splits by them before by harness sha |
| The owner is the bottleneck | Twenty PRs a week nobody reads | The policy caps open proposals (default 3); the report ranks by expected effect; small diffs by construction |
13. What's confirmed vs. inferred¶
| Confirmed (read in this repo) | Inferred (this proposal's judgment) |
|---|---|
| The tables and files named in section 2 exist with the columns described | That a harness hash plus run_outcomes is enough attribution to start; a per-skill "loaded" signal needs transcript parsing that is sketched, not built |
usage_runs carries verify_* from a cc_verify line the dispatcher may write |
How often the dispatcher writes it today (the dispatcher is outside this repo) |
task_links records graph, node, kind and rounds per card |
That rounds and held statuses are clean enough labels without the reason codes proposed in 5.3 |
memory_facts has sources, confidence and supersession; recall is full text plus a line match |
That counters and embeddings will raise the memory hit rate; this needs the phase-5 measurement |
| The guard and the gate fail open and are stdlib-only | That writing one JSON line into the vault keeps them within their 5 s and 10 s timeouts (it should; it is one append) |
| The roster and graph compiler and the dispatcher live outside the engine | That moving the compiler in is a small change; the dispatcher's size is unknown here. The kanban half of this row is no longer inferred: it moved into the engine as the board module (board-design.md) |
| Agents never merge their own PR; shipping needs the owner's approval | That these two rules are sufficient to call the recursion bounded |
Open questions, not researched here
- ~~Should the kanban's cards move into an engine-owned table with agent-hub as one writer (5.3), and what does agent-hub need from the engine for that?~~ Decided and built: the
cardstable, functions and RLS live in the engine as theboardmodule (board-design.md). What remains is the staged rollout (its section 8) and the open questions it lists on its own section 10 (roster rows for lanes, where the Hermes projection lives mid-rollout, theorg_messages.task_idforeign key, review as a status versus a card, lease length). - What is the right noise band? It can only be estimated once phase 3 has baseline-vs-baseline repeats.
- How should cases that need a real repo state (a build card) be fixtured without copying personal code into the scratch vault? A per-case
fixturecommit in the owner's own repos is the current assumption. - Does the Claude subscription allow a third-party runtime to use it, and under what terms? This decides whether the pilot runs on the subscription or on API credits.
- How does Hermes expose per-job prompt versions beyond
jobs.json? If it does not, the engine hashes the synced prompt, which lags by one sync interval.
14. Related work and candidate runtimes¶
Checked against primary sources on 2026-10-24. Where a figure is a vendor's own, it says so. The three systems the owner's summary named (Harness², Meta-Harness, MASS) exist under those names; two of them are a few weeks old and have no independent replication yet.
14.1 Harness and scaffold self-improvement¶
| Work | Mechanism | How it avoids regressions | What this proposal takes from it |
|---|---|---|---|
Harness² (Google Research, arXiv 2610.14224, Oct 2026; code google-research/harness2, Apache 2.0) |
Eight editable harness components around a frozen executor (system prompt, guardrails, memory, tool guidance, subagents, skills, scripts, plugins). Per task: a reference run, then two candidate edits (one for a recurring agentic weakness, one domain-specific), probed on the same task and compared to the reference as a behavioural contrast; a composer merges supported changes or keeps the current harness. An outer "harnessing-level recursion" keeps a library of harnesses, each with an experience note (situation, lessons, evidence), consulted on the next task | No answer key: contrasts inform composition rather than verify correctness, and a lesson is kept only when a contrast supports it. The paper does not use the phrase "zero-regression"; that is a paraphrase of this evidence-gated retention | The propose/probe/compose shape of sections 4.4 to 4.6; contrast on the same task (Tier C); records as the experience library; the authors' own caveat that stochastic runs limit the evidence, which is why this proposal adds repeats, minRuns and a canary. Reported gains of 4 to 12 points on three benchmarks are Google's numbers |
| Meta-Harness (Stanford, arXiv 2603.28052, Mar 2026) | A coding agent proposes a new harness after reading a filesystem holding every prior candidate's source, traces and scores; candidates kept on a score-versus-tokens Pareto frontier; about 60 harnesses over 20 iterations | The proposer never sees the test set; final test on held-out tasks and held-out models | Expose raw traces to the proposer rather than a compressed summary (cli-improve trace); keep cost on the frontier (tokens per pass is a promotion criterion); hold out |
| AIDE / AIDE² (Weco; arXiv 2502.13138 and 2609.26457, blog Jul 2026) | AIDE: tree search over candidate code with evaluated metrics. AIDE²: an outer agent rewrites the inner agent's code; each candidate runs under a fixed dollar budget; each task has a public score the inner agent sees and a private score it cannot; a candidate survives only if it beats the previous best (about 9 in 10 rejected). Reward hacking fell during the run without being an objective | Public versus private score; strict budget; keep only strict improvements | The held-out set under a protected path (principle 5); a weekly budget; counting negative results as output. No public AIDE² code was found; the numbers are Weco's |
| MASS (Sakana AI, arXiv 2610.12176, Oct 2026) | One model as solver (orchestrator plus subagents), evaluator and workflow optimiser. Candidate workflows (roles, instructions, output contracts, hop order) are compared pairwise against a champion, scores from Bradley-Terry, the optimiser revises from the feedback history. Output contracts carry required checks. An outer loop fine-tunes on kept trajectories (out of scope here) | External judges withheld from the loop decide the final measurement; the authors note a same-family judge may overfit to its own criteria | Output contracts on cards and graph nodes (5.1, 5.3); pairwise, position-swapped judging as a diagnostic, never as the sole gate. Note: a 2025 Google paper is also called MASS (Multi-Agent System Search) and is a different thing |
| Darwin Gödel Machine (Sakana / UBC, arXiv 2505.22954) | An archive of coding agents; each rewrites its own code; children evaluated on coding benchmarks and kept in an open-ended archive, not only the best | Human review caught agents faking test logs | Expect verifier gaming; keep a person on the merge; keep an archive of not-promoted records so the search does not collapse to one line |
| ADAS / Meta Agent Search (arXiv 2408.08435) | A meta agent writes new agents in code from a growing archive, evaluated on held-out tasks | Held-out evaluation; transfer across domains and models | Hold out across models too: run_outcomes splits by model before by harness |
| GEPA (arXiv 2507.19457) and MIPROv2 (arXiv 2406.11695) | Reflective prompt evolution over full traces with a Pareto frontier of candidates; Bayesian search over instructions and demonstrations | Frontier, not single best | Traces, not scores, as the proposer's input; small sample sizes handled by keeping several candidates alive |
| Promptbreeder (arXiv 2309.16797) | Evolves task prompts and the mutation prompts that mutate them | The outer loop editing policy.json is this idea applied to mining signatures and thresholds |
|
AlphaEvolve / OpenEvolve (DeepMind blog May 2025; codelion/openevolve) |
Prompt sampler, model ensemble, automated evaluators, evolutionary program database; needs automatically verifiable problems | Automatic evaluators | Where a verifiable check exists (the projects module's verifications, the engine's tests), use it and nothing softer |
| Trace / OPTO (arXiv 2406.16218), TextGrad (arXiv 2406.07496) | Optimisation over execution traces of compound systems; textual feedback propagated through a graph | The graph and its node bodies as the thing being optimised, with per-node metrics |
14.2 Memory and skill libraries¶
| Work | Mechanism | Lesson taken |
|---|---|---|
| ACE: Agentic Context Engineering (Stanford / SambaNova / Berkeley, arXiv 2510.04618) | Context as a playbook of structured bullets with helpful and harmful counters; a Generator produces trajectories, a Reflector distils lessons, a Curator applies small delta updates. Names two failure modes of rewriting memory wholesale: brevity bias and context collapse | Section 7.2 is this design applied to memory/<bot>.md: itemised bullets, counters, one-bullet edits, a curator pass |
| Agent Workflow Memory (arXiv 2409.07429), Voyager (arXiv 2305.16291), SkillWeaver (arXiv 2504.07079) | Induce reusable workflows or executable skills from past trajectories, store them, reuse by retrieval | Skill compilation (7.4): a recurring successful procedure becomes a skill with an eval case |
| Reflexion (arXiv 2303.11366), Dynamic Cheatsheet (arXiv 2504.07952) | Verbal self-reflection stored in episodic memory; self-curated test-time snippets | The weekly reflection already in org-member step 11, given a structure and counters |
| Mem0 (arXiv 2504.19413), A-MEM (arXiv 2502.12110), Letta / MemGPT | Extract, consolidate, retrieve; linked notes; paged memory | Hybrid retrieval and consolidation for memory_facts (7.3). Mem0's benchmark figures are disputed by third-party reimplementations, so none are relied on here |
| Self-evolving agents survey (arXiv 2507.21046) | Evaluation axes: adaptivity, generalisation, efficiency, safety | The metric table in 6.1 covers each axis |
14.3 Topology search¶
AFlow (arXiv 2410.10762, Monte Carlo tree search over code-represented workflows), MaAS (arXiv 2502.04180, a cost-aware supernet that samples an architecture per query) and MAS-GPT (arXiv 2503.03686, a model that emits a multi-agent system per query) all treat the graph as the object of search. For this repo the graph is a yaml file applied per feature, so topology proposals are diffs to that file (5.1), evaluated on graph-level metrics. Per-query dynamic switching, as in the owner's summary, is out of scope until there is more than one graph per kind of work to switch between.
14.4 Anthropic's guidance and the Claude Code surfaces¶
- Building effective agents (Dec 2024): prefer simple workflows (chaining, routing, parallelisation, orchestrator-workers, evaluator-optimiser) over open-ended agents; design the tool interface so mistakes are hard to make. The graph templates are the workflow layer; the card contract is the interface.
- How we built our multi-agent research system (Jun 2025): token use explained most of the quality variance; evaluation started with about 20 queries, an LLM judge with a rubric and pass/fail, human review of edge cases, and end-state evaluation for agents that change state. Twenty cases, end-state checks and a rubric judge as a diagnostic is the shape of Tiers B and C.
- Effective context engineering (Sep 2025): an attention budget, just-in-time retrieval, compaction, structured notes, subagents that return short summaries. Section 7.5 measures these levers instead of guessing them.
- Claude Code hooks (
SessionStart,PreToolUse,PostToolUse,SubagentStart/Stop,PreCompact/PostCompact,Stop,SessionEnd, with JSON decisions andadditionalContext) and subagents (markdown with frontmatter in.claude/agents/, separate context, own tools and hooks) cover what phase 0 needs from the interactive harness: the stamp onSessionStart, hook counts onPreToolUse, compaction events onPreCompact. APreCompacthook that records the context size is a cheap addition toharness/claude/settings.json. - The Agent Skills standard (agentskills.io, Dec 2025):
SKILL.mdwithname(lowercase, hyphens, equal to the folder) anddescription(≤ 1 024 characters), optionallicense,compatibility,metadata, and askills-ref validatetool. The engine's skills already follow it; Tier A should run the reference validator.
14.5 Trace and evaluation tooling¶
- OpenTelemetry GenAI semantic conventions name agent spans
create_agent,invoke_agent,execute_toolandchat. The conventions moved to a separate repository and their stability level could not be confirmed from the primary spec; if the archive ever exports spans, pin the SDK version and maprun → turn → tool callto those names. - Phoenix (OTel-native, open source), Langfuse (self-hosted tracing, prompt management, evals) and Braintrust (eval datasets, judges, CI gating) are the usual hosted or self-hosted choices. This proposal keeps traces in the vault's Postgres, which already has them; an exporter is an option, not a dependency.
- LLM-as-judge: position bias, verbosity bias and self-enhancement bias are documented (arXiv 2306.05685); the swap test (arXiv 2406.07791) accepts only position-consistent verdicts. Tier C follows both and avoids a same-family judge where a second provider is configured.
- Regression suites: Terminal-Bench 2.0 (89 tasks, pinned dependencies, container images, a canary string against contamination), SWE-bench Verified, TheAgentCompany (a simulated company with checkpoint partial credit) and "Best practices for rigorous agentic benchmarks" (arXiv 2507.02825) are the reference for how to build a case: pinned fixtures, an executable check, partial credit by checkpoint, a contamination marker. Golden cases in
.agents/improve/cases/should carry all four.
14.6 Candidate runtimes for the headless pilot¶
| Option | What it is | For | Against |
|---|---|---|---|
Keep claude -p (status quo plus hooks) |
Claude Code headless, driven by the dispatcher | Nothing new to maintain; the owner's interactive setup and the bots share one harness; hooks and subagents exist | Context assembly and compaction are not ours; a complete trace needs the capture proxy; the system prompt and tool list are large and fixed; budgets are enforced from outside |
pi (earendil-works/pi, formerly badlogic/pi-mono; npm @earendil-works/pi-agent-core, pi-ai, pi-coding-agent; MIT) |
A minimal agent core (pi-agent-core: about 2 600 lines, two runtime dependencies) over a unified provider layer (pi-ai, about 40 providers including Anthropic, OpenAI, Google, OpenRouter, Together, Ollama and any OpenAI-compatible endpoint), plus a coding agent with an SDK, an RPC mode, a JSONL event mode, session trees in JSONL files, built-in compaction with a replaceable summary, and an extension API with tool_call (mutate or block), tool_result (redact), session_start, before_agent_start (replace the system prompt and tools), context, session_before_compact and more. Skills follow the Agent Skills standard and are read from .agents/skills, which this repo already uses. Supports Anthropic prompt caching and OAuth login for Claude subscriptions. Version 1.4.0 on 2026-10-21, weekly releases, large community |
Everything in 10.2 maps to an existing hook or SDK call: the guard as a tool_call extension, the stamp on session_start, context assembly in before_agent_start, compaction as policy, session files as traces, typed tools for cli-org and cli-projects. Same language as the app. Reads AGENTS.md natively. Sub-agent extensions exist (pi-subagents packages) and spawn isolated sessions |
No permission system by design (the author calls it security theatre and says to run in a container): the guard would be the only gate, so bot runs need a sandbox (a container or a dedicated Unix user, which the deploy already uses). Extensions run in-process with full OS rights. Ownership moved to a company (Earendil) in 2026 with planned paid layers above the MIT core. Whether Claude subscription use through a third-party runtime is within Anthropic's terms, and whether it bills as extra usage, is unverified; the pilot should assume API credits. Features Claude Code has natively (plan mode, permissions, sub-agent tool) are extensions here |
| Claude Agent SDK | Anthropic's SDK exposing the Claude Code harness programmatically | First-party; keeps Claude Code's tools, hooks, subagents and skills | Not researched for this document; it is the Claude Code harness, so it would not change the context-assembly and tool-list concerns that motivate the pilot |
| Hermes | Already runs cron and chat here, with hooks and a skills directory | Already deployed; the usage-ledger plugin exists | Its loop is its own; the engine syncs its outputs rather than driving it |
Our own loop on pi-ai only |
Use the provider layer, write the loop | Total control | Reinvents pi-agent-core for no gain; rejected |
Status (2026-10-25): this recommendation was acted on. zen-agent is built on @earendil-works/pi-agent-core and pi-ai (not the full coding agent), as a standalone package in this repo's zen-agent/ (own package.json, tests, README — extractable with git subtree split), with: the four pi tools plus typed cli-org, kanban (now including a board backend against the engine's board module) and knowledge-base tools; the guard and the dev-log gate as in-process checks that read the same rule files, tested against the Python suite's cases; context assembly from capped named parts; the card's ## Budget line enforced in the loop; one JSONL session file per run. See zen-agent.md for the full design and what is still open (the roster switch, the dispatcher hand-off, the archive collector, and the subscription-billing question, which remains unresolved — budget it on API credits). It has not yet replaced claude -p for any bot's live wakes, so it is still run "under the deploy's app user in a container or a restricted Unix user" only in the sense that nothing has run it against real card traffic yet.
15. Sources¶
Primary sources were opened on 2026-10-24. Vendor figures are marked as such in the text.
Harness and scaffold self-improvement - Harness²: https://arxiv.org/abs/2610.14224 · https://github.com/google-research/harness2 - Meta-Harness: https://arxiv.org/abs/2603.28052 · https://yoonholee.com/meta-harness/ - AIDE: https://arxiv.org/abs/2502.13138 · https://github.com/WecoAI/aideml · AIDE²: https://arxiv.org/abs/2609.26457 · https://www.weco.ai/blog/first-evidence-of-recursive-self-improvement - MASS (Sakana): https://arxiv.org/abs/2610.12176 · https://github.com/SakanaAI/mass - Darwin Gödel Machine: https://arxiv.org/abs/2505.22954 · ADAS: https://arxiv.org/abs/2408.08435 - GEPA: https://arxiv.org/abs/2507.19457 · MIPROv2: https://arxiv.org/abs/2406.11695 · Promptbreeder: https://arxiv.org/abs/2309.16797 - AlphaEvolve: https://deepmind.google/discover/blog/alphaevolve-a-gemini-powered-coding-agent-for-designing-advanced-algorithms/ · OpenEvolve: https://github.com/codelion/openevolve - Trace/OPTO: https://arxiv.org/abs/2406.16218 · TextGrad: https://arxiv.org/abs/2406.07496
Memory and skills - ACE: https://arxiv.org/abs/2510.04618 · Agent Workflow Memory: https://arxiv.org/abs/2409.07429 · Voyager: https://arxiv.org/abs/2305.16291 · SkillWeaver: https://arxiv.org/abs/2504.07079 - Reflexion: https://arxiv.org/abs/2303.11366 · Dynamic Cheatsheet: https://arxiv.org/abs/2504.07952 - Mem0: https://arxiv.org/abs/2504.19413 · A-MEM: https://arxiv.org/abs/2502.12110 · Letta/MemGPT: https://www.letta.com/blog/memgpt-and-letta/ - Self-evolving agents survey: https://arxiv.org/abs/2507.21046
Topology search - AFlow: https://arxiv.org/abs/2410.10762 · MaAS: https://arxiv.org/abs/2502.04180 · MAS-GPT: https://arxiv.org/abs/2503.03686
Anthropic - https://www.anthropic.com/engineering/building-effective-agents · https://www.anthropic.com/engineering/multi-agent-research-system · https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents - Hooks: https://code.claude.com/docs/en/hooks · Subagents: https://code.claude.com/docs/en/sub-agents · Agent Skills: https://agentskills.io/specification
Evaluation and tracing - LLM-as-judge: https://arxiv.org/abs/2306.05685 · https://arxiv.org/abs/2406.07791 · https://arxiv.org/abs/2412.05579 - Terminal-Bench 2.0: https://arxiv.org/abs/2601.11868 · TheAgentCompany: https://arxiv.org/abs/2412.14161 · AgentBench: https://arxiv.org/abs/2308.03688 · Rigorous agentic benchmarks: https://arxiv.org/abs/2507.02825 · SWE-bench Verified: https://openai.com/index/introducing-swe-bench-verified/ - OpenTelemetry GenAI: https://github.com/open-telemetry/semantic-conventions-genai
pi
- Repository and docs: https://github.com/earendil-works/pi (the former badlogic/pi-mono redirects here) · https://pi.dev/ · https://pi.dev/news/2026/5/7/pi-has-a-new-home
- Design posts: https://mariozechner.at/posts/2025-11-30-pi-coding-agent/ · https://mariozechner.at/posts/2025-11-02-what-if-you-dont-need-mcp/ · https://earendil.com/posts/pi-1-0/
This repo, since this document was written - zen-agent (the headless-runtime pilot this document's section 10 asked for): zen-agent.md · kon-rad/argo#38 · #40 - The board module (the kanban-in-the-engine design this document's section 5.3 asked for): board-design.md · kon-rad/argo#39