Skip to main content

Agent evaluation

The agent evaluation in scripts/skilleval measures whether coding agents find, apply and grow a project's context through kapi. Agents do ordinary writing tasks in a documentation repository whose conventions are written down as an answer key, and the harness scores what they wrote and what they recorded against that key. The paired agent evaluation compares integrations across one task; this evaluation holds the integration fixed and asks what an agent does with a project's context.

The commands, the order to run them in and how to read a report are in the runbook, docs/internals/agent-evaluation.md.

The fixture and its key​

The fixture is the help centre of Loomwise, an invented rota app for cafés and small shops: a README and eight pages of varied prose. Its answer key names twelve entries:

KindEntries
Namesthe product, a feature and two plan names, each written one way
SpellingsBritish spelling throughout, "sign in" rather than "log in", "email" rather than "e-mail"
Avoided words"roster" and "employee", with "rota" and "team member" used instead
Habitsthe reader is addressed as "you"; no exclamation marks
Decoy"timesheet" and "time sheet", used both ways about equally

The first eleven are conventions. The decoy is something the project is inconsistent about, and it must never become a rule.

Code generates the fixture, and generation is deterministic, so every run sees the same bytes. The key is the one source of truth: names enter the prose only through the key's own tokens, and generation checks the rendered pages against the key before a cell is built. Every name and spelling appears several times across several files, no avoided word or misspelt name appears anywhere, every page addresses the reader as "you", and the decoy's two forms stay roughly even. A page that broke any of these fails generation, which is how the fixture is kept from contradicting its own key. The prompts are checked the same way: none may mention kapi, context, rules or checking, or name a form of any convention.

Three writing tasks run on the fixture: a new help page for a feature, a monthly release note, and a troubleshooting section.

What one cell is​

A cell is one host working one task, in its own copy of the fixture with its own kapi roots. Every cell is prepared the same way: the fixture under git, the real kapi init --agents all run over it by the binary under test, and the content mapping its scaffold leaves for a person. A Measure 1 cell also has the planted conventions put in force by a person before the agent starts. The commit created after preparation is the cell's baseline.

Cells are generated outside this repository. An agent host walks up from its working directory looking for CLAUDE.md, and kapi walks up looking for kapi.yaml, so a cell inside the tree would bind to neokapi's own project. Preparation reports every discoverable file above a cell, and anything found blocks the batch.

Each cell keeps its own KAPI_DATA_DIR, KAPI_CONFIG_DIR, XDG_DATA_HOME, XDG_CACHE_HOME and plugin root, with KAPI_PLUGINS_DIR_ONLY set, a fresh HOME, and a private PATH holding ordinary editing tools and this checkout's kapi under each name the skill drives. The .mcp.json that kapi init writes names a bare kapi; preparation resolves that name on the cell's PATH and blocks the batch unless it is the build under test. Codex cells get the trust entry a person would accept and the environment forwarding Codex needs, and preflight asks Codex itself what it sees. A digest of the person's own data root is taken before and after every session. Process-level confinement is not claimed.

Measure 1: agents apply context​

The eleven conventions are established as rules with KAPI_ACTOR=person before the session. Preparation reads the context log back and refuses a cell in which any rule did not reach the held status.

While the session runs, the harness polls the working tree and keeps the first saved version of every file the agent changed. Afterwards it runs kapi check --diff-against <baseline> --json twice: over the final state, and over the first versions laid on the baseline. A finding fails when its severity does, which leaves out minor, neutral and unconfirmed advice. Each failing vocabulary finding is traced to the convention whose rule it names.

A convention applies to a run when the text the agent added uses one of its forms, correct or avoided, or when a finding names it. The two habits apply to any added prose. The transcript says whether the agent ran a check after its last write.

Acceptance criteria: 90% of applicable conventions followed in the first saved version, and no failing finding at the end.

Measure 2: agents grow context​

These cells start without established rules. The agent's entries in kapi context log --json are scored against the answer key. Entries by a person and decisions such as confirmations are left out.

  • A rule matches a convention when its replacement is a form the convention writes, when its term is a form the convention avoids, or, for British spelling, when the two sides are one word's American and British spellings.
  • An observation matches when it names an avoided form or one of the convention's cues, and, for a name, when it names the name and says how it is written.
  • The decoy is proposed when a rule names either of its forms on either side, or when an observation names a form and tells a writer what to do with it.

An entry outside the key is judged by a stated rule rather than by a model. A rule is harmless when the project already obeys it and it points at wording the project uses: its term appears nowhere in the fixture and its replacement does. An observation is harmless when it carries evidence and every piece of that evidence names a fixture file or quotes the fixture's own words. Anything else is noise.

Recall counts the four names and three spellings a run recorded. Precision is the share of entries that match the key or are harmless. Acceptance criteria: on average half the names and spellings recorded per run, 80% precision over every entry, and the decoy never proposed.

Measure 3: settling​

Measure 3 needs no live run. eval_settle.go scripts a story across four machines that start from one agent session's suggestions: a second session that repeats one rule and suggests a rival for another, CI recording a merge to the default branch, a person's corrections, and a person's digest that keeps one suggestion and drops another. Each machine settles its own log. The measure then merges the four logs into a fresh workspace in all 24 orders, settling after each merge, and is met when every order establishes exactly the expected rules and every rule ends at the same status. The scenario lives in eval_product.go with the rest of the product's vocabulary.

TestEvalSettle_MeasureIsMet runs it with the framework tests in CI, and every report measures it afresh, with a table of each rule's status.

Measure 4: review usefulness​

After the grow runs, the report writes a review sheet: every entry each run recorded, grouped under the convention it matches, then the decoy, then entries outside the key, each with its evidence. It ends with three questions: how long reading it took, which entries are worth keeping, and whether the entries showed anything about the fixture that was not planted. A person answers in a small YAML file, and the next report records the answers.

Where the product's vocabulary lives​

The evaluation measures kapi as it stands when it runs. How a person puts a rule in force, what a held rule's status is called and which severities fail are in eval_product.go. How each MCP tool counts (asking, recording, checking or writing) is one table in eval_transcript.go. Preparation reports a server tool that table cannot place, so a renamed tool shows up before a run rather than as a silent zero in a report.

Evidence handling​

Transcripts, both versions of every changed file, the context log, review sheets and the answer key stay in ignored local output under harness/out/eval. Every attempt is bound to a fingerprint over the manifest, the fixture with its key and prompts, the runner's source, the shipped skill and the kapi binary, so a changed input calls for a fresh evidence directory rather than a mixed study. The live phases share one persistent attempt ceiling, which counts failed attempts too and never resets, and nothing retries automatically. Reports are rendered from saved attempts with no model call, so a change to the scoring reaches runs already made.