Writing Tests

Benchmark tests are standard Bun test files in tests/*.bench.ts. No benchmark-specific runtime wrapper, no special harness — you boot Axon() exactly as you would in an ordinary agent test. The bench runner applies the current cell's factors (agent, model, environment) to that boot automatically. Your test doesn't know which cell it's in, and that's the point.

import { afterEach, describe, expect, it } from "bun:test"
import { Axon } from "@axon/core"

describe("repository navigation", () => {
    it("finds the production database configuration", async () => {
        const runtime = await Axon()

        const answer = await runtime.axon.request(
            "Find the production database configuration and explain which value selects its connection pool.",
        )

        const result = inspectAnswer(answer)
        const dimensions = { difficulty: "hard", language: "typescript" } as const

        bench.observe("success", result.correct, { dimensions })
        bench.observe("files_examined", result.filesExamined, { dimensions })
        bench.observe("strategy", result.strategy, { dimensions })

        await bench.attach("answer", result.answer)

        expect(result.correct).toBe(true)
    })
})

bench.observe() — one verb for every measurement

Tests emit observations by measurement id. One semantic verb covers scalars, categories, and series — the config's declaration supplies the type, unit, objective, and aggregation, so the call site stays bare:

bench.observe("success", result.correct, { dimensions })

// step-grain measurements take a position
result.confidence.forEach((value, step) => {
    bench.observe("confidence", value, { dimensions, at: { step } })
})

Observing a measurement the config doesn't declare is an error. The contract is the vocabulary; tests can't invent numbers the results table wouldn't know how to read.

bench.attach() — evidence

The second and final output primitive. Attach the declared artifacts — the subject's answer, produced files, transcripts — and the harness hashes, persists, and correlates them with the run. Authors never manufacture references or manage storage.

await bench.attach("answer", result.answer)

Assertions still mean what they mean

expect() is not scoring. A failed assertion is a hard test failure — Bun owns that semantic, exactly as in any test suite. An observed score is data about how well the subject did. The two are related evidence, but they are not the same thing:

bench.observe("success", result.correct, { dimensions })  // the score, always recorded
expect(result.correct).toBe(true)                          // the hard failure, if it isn't

A benchmark case can observe a poor score and still pass — or fail hard because the subject crashed, which is itself information. Keep the two channels distinct and the results stay honest.

The workspace is already there

By the time a test runs, the cell's workspace has been created from template/ and the subject operates inside it. Sessions produced by the run are attached to the case automatically — when a result looks wrong, the full trace of what the agent actually did is on the record.