Documentation
¶
Overview ¶
Package eval is the evaluation harness for the agent loop (HP-11): it turns a stochastic, multi-turn agent into MEASURABLE data — a pass-rate distribution, a cost, a convergence curve, and an honesty number — so a change to the context policy, the prompt, or the toolset can be shown to help instead of merely felt to.
It is the consumer the agent package was shaped for: Run/RunNative already emit a typed RunResult (Outcome + Answer + Step trace + Usage) and take a silent Observer, and RunResult.Root already travels with the result as "the fixture hook". This package supplies the four things the dogfood bake-offs (docs/findings/DOGFOOD.md R9/R10) proved a serious eval needs and manual dogfooding lacked:
- An INDEPENDENT oracle — grading is a separate judgment from the agent's self-reported Outcome. Models reported `answered` with the build red; "did it say done" and "is it actually done" are different questions and only the Oracle answers the second.
- HELD-OUT cases — the grader runs inputs the model never saw, so a parser that passes the handed suite but silently accepts "2+3$" is still failed.
- N runs per cell — a single trial measures the dice (R9→R10 flipped the same model pass↔fail), so the unit of measurement is a distribution, not a bool.
- Pristine fixtures — every Trial starts from a byte-identical copy in a fresh temp dir, never the live repo and never a dir a prior trial mutated.
This file holds the case + fixture vocabulary; oracle.go grades, trial.go runs one (Case × Model) execution, and report.go aggregates and archives.
Index ¶
- Constants
- Variables
- func AggregateReviewFates(recs []agent.RunRecord) map[string]*ReviewFateStats
- func CostOf(model string, u llm.Usage) (cost float64, ok bool)
- func RenderReviewFates(stats map[string]*ReviewFateStats) string
- func SelectBest(trials []Trial) int
- func WarmPricing()
- type Case
- type Cell
- func (c Cell) Best() (Trial, bool)
- func (c Cell) CostP(p float64) (cost float64, ok bool)
- func (c Cell) CostTotal() (total float64, ok bool)
- func (c Cell) FalsePositiveRate() float64
- func (c Cell) FalsePositives() int
- func (c Cell) Infra() int
- func (c Cell) ItersP(p float64, onlyPass bool) int
- func (c Cell) LadderCostByModel() map[string]float64
- func (c Cell) LadderEscalationRate() float64
- func (c Cell) LadderEscalations() int
- func (c Cell) LadderMeanAttempts() float64
- func (c Cell) LadderN() int
- func (c Cell) LadderTouchRateByModel() map[string]float64
- func (c Cell) LatencyP(p float64) int64
- func (c Cell) N() int
- func (c Cell) Outcomes() map[agent.Outcome]int
- func (c Cell) PassRate() float64
- func (c Cell) PassRateCI() (lo, hi float64)
- func (c Cell) PassRateInfraExcluded() float64
- func (c Cell) Passes() int
- func (c Cell) PricingCoverage() string
- func (c Cell) PromptTokensP(p float64) int
- func (c Cell) ReasoningTokensP(p float64) int
- func (c Cell) ReviewStatuses() map[string]int
- func (c Cell) Split() bool
- func (c Cell) UnpricedModels() []string
- type CommandOracle
- type Fixture
- type GitFixture
- type Grade
- type GradeInput
- type HeldOut
- type Manifest
- type MapFixture
- type Model
- type Oracle
- type Price
- type Report
- type ReviewFateStats
- type SetupResult
- type TreeEntry
- type Trial
- type TrialLadder
- type TrialRunFunc
Constants ¶
const ReportSchemaVersion = "6"
ReportSchemaVersion is the current eval-report schema. v2 added LatencyMs on Trial and the reason/latency columns; v3 added Trial.NoAttempt (Grade. NoAttempt) and the best-of-N selection section; v4 added infra tracking and plan-stage cost; v5 added ladder metadata and escalation guard metrics; v6 added task-steer arm metadata. Bump on any further shape change.
Variables ¶
var Pricing = map[string]Price{
"openai/gpt-5.5": {InPerM: 5.00, OutPerM: 30.00, CacheReadPerM: 0.50},
"openai/gpt-5.6-sol": {InPerM: 5.00, OutPerM: 30.00, CacheReadPerM: 0.50},
"openai/gpt-5.6-terra": {InPerM: 2.50, OutPerM: 15.00, CacheReadPerM: 0.25},
"openai/gpt-5.6-luna": {InPerM: 1.00, OutPerM: 6.00, CacheReadPerM: 0.10},
"openai/gpt-5.6-sol-pro": {InPerM: 5.00, OutPerM: 30.00, CacheReadPerM: 0.50},
"openai/gpt-5.6-terra-pro": {InPerM: 2.50, OutPerM: 15.00, CacheReadPerM: 0.25},
"openai/gpt-5.6-luna-pro": {InPerM: 1.00, OutPerM: 6.00, CacheReadPerM: 0.10},
"anthropic/claude-fable-5": {InPerM: 10.00, OutPerM: 50.00, CacheReadPerM: 1.00},
"qwen/qwen3.7-max": {InPerM: 1.20, OutPerM: 6.00},
"anthropic/claude-opus-4.8": {InPerM: 5.00, OutPerM: 25.00, CacheReadPerM: 0.50},
"google/gemini-3.1-pro-preview": {InPerM: 2.00, OutPerM: 12.00, CacheReadPerM: 0.20},
"claude-opus-4-8": {InPerM: 5.00, OutPerM: 25.00, CacheReadPerM: 0.50},
"claude-opus-4-7": {InPerM: 5.00, OutPerM: 25.00, CacheReadPerM: 0.50},
"claude-sonnet-5": {InPerM: 3.00, OutPerM: 15.00},
"claude-haiku-4-5": {InPerM: 1.00, OutPerM: 5.00, CacheReadPerM: 0.10},
"deepseek/deepseek-v4-flash": {InPerM: 0.09, OutPerM: 0.18, CacheReadPerM: 0.018},
"tencent/hy3-preview": {InPerM: 0.30, OutPerM: 1.20},
"google/gemini-2.5-flash-lite": {InPerM: 0.10, OutPerM: 0.40},
"anthropic/claude-opus-4.7": {InPerM: 5.00, OutPerM: 25.00, CacheReadPerM: 0.50},
"moonshotai/kimi-k2.6": {InPerM: 0.60, OutPerM: 2.50},
"moonshotai/kimi-k2.6:free": {InPerM: 0.00, OutPerM: 0.00},
"google/gemini-3-flash-preview": {InPerM: 0.50, OutPerM: 3.00, CacheReadPerM: 0.05},
"x-ai/grok-4.3": {InPerM: 1.25, OutPerM: 2.50},
"x-ai/grok-4.20": {InPerM: 1.25, OutPerM: 2.50},
"z-ai/glm-5": {InPerM: 0.60, OutPerM: 1.92, CacheReadPerM: 0.12},
"z-ai/glm-5.2": {InPerM: 0.909, OutPerM: 2.856, CacheReadPerM: 0.169},
"openai/gpt-5.4": {InPerM: 2.50, OutPerM: 15.00},
"openai/gpt-5.2-codex": {InPerM: 1.75, OutPerM: 14.00},
"anthropic/claude-sonnet-4.6": {InPerM: 3.00, OutPerM: 15.00},
"claude-fable-5": {InPerM: 10.00, OutPerM: 50.00, CacheReadPerM: 1.00},
"qwen/qwen3-coder": {InPerM: 0.22, OutPerM: 1.80},
"minimax/minimax-m3": {InPerM: 0.30, OutPerM: 1.20},
"google/gemini-3.5-flash": {InPerM: 1.50, OutPerM: 9.00},
"moonshotai/kimi-k2-thinking": {InPerM: 0.60, OutPerM: 2.50},
"deepseek/deepseek-v4-pro": {InPerM: 0.435, OutPerM: 0.87, CacheReadPerM: 0.0036},
}
Pricing is the hand-maintained price table for the eval roster, USD per 1M tokens, in/out + cache-read refreshed from live OpenRouter 2026-07-05. It is a STATIC, in-repo table by design, not a live fetch: a report is a git-pinned, reproducible artifact (see report.go's Manifest), so the cost column must be pinned too — a price that moved after a run must not silently rewrite a past report's numbers. Update this table when the roster or its prices change; a live source can later drop in behind CostOf without touching its callers.
A slug absent here renders "—" (unknown), never $0 — free and unpriced are different facts, and conflating them would make an un-priced model look like the cheapest in the table.
Functions ¶
func AggregateReviewFates ¶
func AggregateReviewFates(recs []agent.RunRecord) map[string]*ReviewFateStats
AggregateReviewFates folds every record's review report into per-reviewer stats. Records without a report (gate off) are skipped; a report without a reviewer model aggregates under "(unknown)".
func CostOf ¶
CostOf returns the USD cost for a model's usage and whether the model was priced at all. ok=false means the slug is absent from Pricing — the caller renders "—", not 0, because free and unknown are different facts.
func RenderReviewFates ¶
func RenderReviewFates(stats map[string]*ReviewFateStats) string
RenderReviewFates renders the aggregate as a stable, scannable table (one line per reviewer, name-sorted) for reports and the CLI.
func SelectBest ¶
SelectBest returns the index into trials of the structurally-best trial, breaking rank ties by the earliest trial Index (deterministic, and no reason to prefer a later attempt). -1 when trials is empty.
func WarmPricing ¶ added in v0.2.0
func WarmPricing()
WarmPricing eagerly loads the live OpenRouter price catalog (once). Call it from a background goroutine at process startup: CostOf's live-catalog fallback otherwise blocks the FIRST cost lookup — which sits on the agent loop's hot path between a model response and its tool dispatch — on a synchronous HTTP fetch (measured 200ms online, up to the 5s client timeout offline).
Types ¶
type Case ¶
type Case struct {
Name string
Task string // the goal handed to the agent.
// TaskSteer, when non-empty, is appended verbatim to Task by RunTrial as a
// final paragraph. It is an eval-arm knob rather than part of a case's task.
TaskSteer string
Fixture Fixture // the starting state, materialized fresh per trial.
Oracle Oracle // grades the finished run against ground truth (not self-report).
Protocol string // "tools" (default) or "text".
Config agent.Config // knob template (MaxIterations, MaxTokens, RunTimeout, Verify*, …).
// Sandbox, when non-nil, builds the per-trial sandbox over the freshly
// materialized fixture dir — the seam for cases whose execution environment is
// not the host (SWE-bench instances exec inside their official per-instance
// Docker image, rooted at the extracted /testbed). The trial owns the returned
// sandbox's lifetime (Close). nil => the default host-local sandbox.
Sandbox func(ctx context.Context, dir string) (sandbox.Sandbox, error)
// Setup, when non-nil, runs after the fixture is materialized and the
// per-trial sandbox exists, but before the agent/runner starts. It can do
// case-specific in-sandbox measurement and return a per-trial LadderVerify
// command. The returned command overrides LadderVerify for this trial,
// including the empty string (used to refuse ladder runs whose signal could
// not be validated).
Setup func(ctx context.Context, root string, sb sandbox.Sandbox) (SetupResult, error)
// Tools, when non-nil, builds the agent's toolset from the per-trial sandbox.
// It is the seam for replaying a case under the PRODUCTION toolset of the binary
// it mirrors: commit-msg and issue-bot run ReadOnlyTools (no write/edit), not the
// full DefaultTools the loop falls back to — so without this a "review" case
// would be handed mutation tools it never has in production, and could behave
// (and be graded) unfaithfully. nil => the loop's DefaultTools.
Tools func(sb sandbox.Sandbox, runTimeout time.Duration) map[string]agent.Tool
// VCSWorkspace is true when the fixture materializes a real git work tree
// suitable for VCS-backed runners such as the escalation ladder. Pure
// in-memory fixtures (MapFixture) must leave this false so the ladder arm
// can reject them cleanly rather than half-working.
VCSWorkspace bool
// LadderVerify is the in-run command the ladder uses as the red/green signal
// for each rung attempt. When empty, the case verifies only via the held-out
// oracle and is NOT ladder-compatible — validateLadderCases rejects it. The
// case constructor sets this when it can provide a meaningful per-trial
// verify command (e.g. a suite that the agent can pass by completing the
// task). It is independent of the CLI -verify-cmd flag, which is an optional
// self-check for the single-model path and is not a substitute for a
// case-declared ladder signal.
LadderVerify string
}
Case is one task the harness can run against any model: the prompt, the pristine starting state (Fixture), the independent grader (Oracle), and the agent knobs to run it under (Config — minus the per-trial fields the runner fills: Model, Sandbox, Root, Obs, Task). Protocol picks the loop, mirroring cmd/agent: "tools" (default, native function-calling) or "text". A "tools" case refuses to run (infra failure) on a model that lacks tool support, instead of falling back to the text loop, to avoid measurement confounds.
type Cell ¶
type Cell struct {
Case string
Model string
Trials []Trial
// PricingCoverage_ and UnpricedModels_ are additive report fields
// populated by WriteFiles before JSON serialisation so downstream
// consumers of report.json can see per-cell pricing honesty without
// recomputing from trials. The methods PricingCoverage() and
// UnpricedModels() compute the same values on the fly for callers that
// don't go through WriteFiles (tests, markdown).
PricingCoverage_ string `json:"pricing_coverage,omitempty"`
UnpricedModels_ []string `json:"unpriced_models,omitempty"`
}
Cell is the n trials of one (Case × Model) pair — the unit a metric is computed over. The methods are the report's vocabulary: a pass-rate with a confidence interval (not a bare fraction), the false-positive rate (the honesty number), the outcome histogram, and cost/convergence percentiles.
func (Cell) Best ¶
Best returns the cell's selected trial (see SelectBest). ok is false for an empty cell.
func (Cell) CostP ¶
CostP is the pth percentile of per-trial USD cost across the cell's PRICED trials — the comparable cost-per-run number (a slow, token-hungry passer is expensive even at a cheap per-token rate). ok=false when no trial was priced, so the report renders "—" rather than a misleading $0.
func (Cell) CostTotal ¶
CostTotal sums the USD cost of every priced trial in the cell — what this cell actually cost to measure, reviewer bill included (R4b's sweep-cost line underreported 6.5× by pricing only the solver). ok=false when nothing was priced. The $/trial percentile column stays solver-only on purpose: it compares MODELS, while this compares BILLS.
Ladder trials: t.Cost is already all-in (solver + reviewer + planner summed by ladder.priceAttempt across every attempt), so PlanCost/ReviewCost are NOT added — they hold the WINNING attempt's components and would double-count.
func (Cell) FalsePositiveRate ¶
FalsePositiveRate is FalsePositives/N — the metric the closing verification gate (VerifyCmd) is meant to drive toward zero.
func (Cell) FalsePositives ¶
FalsePositives counts trials where the agent claimed done (Answered) but the oracle failed it — the dangerous failure the bake-offs surfaced.
func (Cell) ItersP ¶
ItersP returns the pth percentile (0..100) of iteration counts. onlyPass restricts to passing trials — convergence is only meaningful for runs that actually solved the task (a run that looped to the cap tells you nothing about how fast success arrives).
func (Cell) LadderCostByModel ¶
LadderCostByModel aggregates CostByModel across all ladder trials in this cell. Unpriced models are absent from the map. Returns nil when there are no ladder trials or no per-model data.
func (Cell) LadderEscalationRate ¶
LadderEscalationRate is escalations / ladder-trials. 0 when no ladder trials.
func (Cell) LadderEscalations ¶
LadderEscalations counts ladder trials that escalated past rung 1.
func (Cell) LadderMeanAttempts ¶
LadderMeanAttempts returns the mean number of attempts across ladder trials. 0 when no ladder trials.
func (Cell) LadderN ¶
LadderN counts trials in the cell that have ladder metadata (i.e. were run by a ladder arm). 0 for non-ladder cells.
func (Cell) LadderTouchRateByModel ¶
LadderTouchRateByModel returns, for each model, the fraction of ladder trials in this cell where that model had nonzero cost. Returns nil when there are no ladder trials.
func (Cell) LatencyP ¶
LatencyP is the pth percentile of per-trial agent wall-clock (ms) across the cell — the time axis, told apart from token spend: a model can be cheap per token yet slow per run (high reasoning latency, slow tools). 0 when no step timing was captured (e.g. a pre-timing trace replay).
func (Cell) Outcomes ¶
Outcomes is the histogram of self-reported terminal states across the cell — answered / hit_cap / killed_* / unverified — so a failing cell shows HOW it failed (looped to cap vs killed by a detector vs a false answer).
func (Cell) PassRateCI ¶
PassRateCI is the Wilson 95% interval for the pass-rate — honest about small N. With 7/10 passing it reports ≈[0.40, 0.89], so "0.7" is never mistaken for a precise measurement off ten noisy samples.
func (Cell) PassRateInfraExcluded ¶
PassRateInfraExcluded is (Pass && Err == "") / (N - Infra). 0 when N == Infra.
func (Cell) PricingCoverage ¶
PricingCoverage classifies the cell by how completely its trials are priced: "full" (every trial has all applicable components priced), "partial" (some component priced, some not), "none" (nothing priced). For ladder trials the Ladder.Priced flag is the single all-in gate — ladder.Cost already includes solver + reviewer + planner.
func (Cell) PromptTokensP ¶
PromptTokensP is the pth percentile of prompt tokens across all trials — the cost axis (capability shows up as token spend, not just pass/fail).
func (Cell) ReasoningTokensP ¶
ReasoningTokensP is the pth percentile of reasoning tokens across all trials — where a thinking model's completion spend actually goes (a subset of completion tokens, already billed). 0 for non-thinking models, which is the useful signal.
func (Cell) ReviewStatuses ¶
ReviewStatuses aggregates the status values of every trial's ReviewReport into a count histogram. Returns nil when no trial carries a review report (gate was off for the whole cell). Ladder trials only contribute the top-level trial Review; per-attempt review reports are not carried on AttemptRecord, so the histogram aggregates top-level review statuses.
func (Cell) Split ¶
Split reports whether the cell mixes passing and failing trials — the only cells where selection has any work to do, and the denominator of the selection-accuracy metric.
func (Cell) UnpricedModels ¶
UnpricedModels returns the deduplicated set of model ids that were seen in this cell but not priced. For non-ladder trials each unpriced component contributes its model id (solver, planner, reviewer). For ladder trials only the top-level Priced flag is available — individual models inside the ladder cannot be distinguished — so the arm label itself is reported when unpriced.
type CommandOracle ¶
type CommandOracle struct {
Label string // short name for the report Detail (e.g. "suite").
Cmd string // the success command; exit 0 == pass.
Timeout time.Duration // 0 = defaultOracleTimeout.
}
CommandOracle passes iff a shell command exits zero in the post-run fixture. It is the domain-specific verifier HP-5 calls for where one exists — "go test ./...", a build, a property check — run through the sandbox so it sees exactly what the agent left on disk. Label names it in multi-oracle Detail output.
func (CommandOracle) Grade ¶
func (o CommandOracle) Grade(ctx context.Context, in GradeInput) Grade
Grade runs Cmd and passes only on a zero exit. A command that fails to start is a non-pass with the start error as Detail (we could not confirm success).
type Fixture ¶
Fixture materializes a case's pristine starting state into a fresh directory. Materialize is ADDITIVE (it writes files, never deletes), so the same type also serves HeldOut's drop-in step: dropping a held-out test into an already-populated run dir is just another Materialize over the same dir.
type GitFixture ¶
type GitFixture struct {
RepoRoot string // path to the live git repo to archive FROM (the source of truth).
Ref string // commit-ish to materialize (e.g. "ba3ba65^" for "the parent of the fix").
}
GitFixture materializes a REAL repository tree at a pinned commit into the trial dir — the self-history ingest path (HP-11 task diversity). Where MapFixture holds a hand-authored toy in-code, GitFixture archives the actual bytes the repo had at a base commit, so a case can be "the repo as it stood before commit X, minus the fix" — a genuine multi-file task that exercises cross-file navigation and triggers the context-window faces of HP-1 that a <100-LOC toy never can. It is additive (it only writes files), so a HeldOut oracle still layers its dropped tests on top exactly as for MapFixture.
The materialized tree is a hermetic build IFF its module deps resolve from the host cache. The one thing that does NOT survive a copy out of the live repo is a RELATIVE replace directive (e.g. `replace X => ../mneme`): the sibling it points at is not beside the temp dir. Materialize rewrites every relative filesystem replace to an ABSOLUTE path resolved against RepoRoot (where the original go.mod lived, so the relative target meant RepoRoot/../mneme), which keeps the local-dep build working without a per-module allowlist.
func (GitFixture) Describe ¶
func (g GitFixture) Describe() string
Describe names the pinned source state for the manifest/logs.
func (GitFixture) Materialize ¶
func (g GitFixture) Materialize(dir string) error
Materialize streams `git archive <ref>` from RepoRoot into dir, then fixes up relative replace directives so the local-dep build resolves from the temp dir.
type Grade ¶
type Grade struct {
Pass bool
Detail string
// NoAttempt marks a run that left NOTHING gradable — e.g. an unchanged
// working tree (SWE-bench's empty model patch). Distinct from a wrong
// attempt: best-of-N selection (SelectBest) demotes these below every trial
// that did work, because an `answered` over a no-op is a confident no-op.
NoAttempt bool
}
Grade is an oracle's verdict: did the run actually satisfy the case, and why. Detail is the evidence — the failing held-out test, the wrong answer — so a failed cell in the report is diagnosable without re-running.
type GradeInput ¶
type GradeInput struct {
Root string
Sandbox sandbox.Sandbox
Result *agent.RunResult
// SetupCreated records untracked paths that Setup created, keyed by path.
// The entry is the complete post-Setup git tree identity; an oracle may
// exclude a path only while its current entry remains exactly this value.
SetupCreated map[string]TreeEntry
}
GradeInput is everything an oracle needs to judge a run. Root is the fixture dir AFTER the run — the real on-disk effect to inspect. Sandbox is the same fence the agent used, so an oracle runs its checks through the identical boundary (and rooted at the same dir). Result carries the agent's typed outcome and trace: read it, never trust it as the verdict.
type HeldOut ¶
type HeldOut struct {
Label string // names this oracle in Detail (e.g. "held-out").
Drop Fixture // the held-out files, written into Root after the run.
Verify string // the command run over the augmented fixture; exit 0 == pass.
Timeout time.Duration // 0 = defaultOracleTimeout.
}
HeldOut is the suite-green-≠-correct guard (DOGFOOD R9/R10): it Materializes extra files the agent NEVER saw — novel/adversarial cases — into the finished fixture, then runs Verify over the augmented suite. A run that overfit the handed tests (grok's parser that accepted "2+3$") passes the original suite but fails here. Drop is additive (it only adds files), so the original suite still runs alongside the held-out cases.
type Manifest ¶
type Manifest struct {
SchemaVersion string `json:"schema_version"` // bump when Trial/Cell/Manifest shape changes, so a diff tool can refuse to compare incompatible reports instead of misreading absent-vs-zero fields.
GeneratedAt string `json:"generated_at"`
GitSHA string `json:"git_sha"`
GoVersion string `json:"go_version"`
TrialsPer int `json:"trials_per_cell"`
Models []string `json:"models"`
Cases []string `json:"cases"`
TaskSteerArmed bool `json:"task_steer_armed,omitempty"`
}
Manifest is the reproducibility record — pinned ids and settings so a report is a comparable artifact, not a one-off. The CLI fills it (it owns time, git, and the env), keeping this package pure and testable.
type MapFixture ¶
MapFixture is the simplest fixture: a path→content map written verbatim into the target dir (parent dirs created as needed). Paths are slash-separated and relative to the dir root. It keeps a small fixture in-code and hermetic — no embed, no nested-module embed restriction (a fixture's own go.mod is just a "go.mod" key), no testdata build interplay. A large fixture is better served by a future embed-backed DirFixture; this is what the first slice ships.
func (MapFixture) Describe ¶
func (m MapFixture) Describe() string
Describe lists the fixture's paths in sorted order (a stable, scannable summary for the manifest / logs).
func (MapFixture) Materialize ¶
func (m MapFixture) Materialize(dir string) error
Materialize writes every entry, creating parent directories first.
type Model ¶
type Model struct {
Label string
Provider llm.Provider
Run TrialRunFunc
RequiresVCS bool
}
Model pairs a provider with the human-facing id used in the report. The provider's Name() is its registered identity ("openrouter"), not the model string the run actually exercised ("openai/gpt-4o-mini"), so the label is carried explicitly — the report compares models, and a row needs the real id.
When Run is set, RunTrial delegates the solve entirely to this function, skipping the normal provider/protocol selection. RequiresVCS gates the pre-run VCS initialisation needed by runners that depend on a clean git work tree (e.g. the ladder).
type Oracle ¶
type Oracle interface {
Grade(ctx context.Context, in GradeInput) Grade
}
Oracle grades a finished run against GROUND TRUTH, independent of what the agent claimed. This separation is the harness's whole reason to exist: the bake-offs (DOGFOOD R9/R10) showed models report Outcome=Answered while the build is red, so the agent's self-report and the truth are different signals. An Oracle answers only the second question, the same way a human grader did in the dogfood rounds — but reproducibly and at N-run scale.
func AllOf ¶
AllOf passes iff every oracle passes, evaluated IN ORDER and short-circuiting on the first failure (whose Detail it returns). Order matters: a CommandOracle on the original suite placed before a HeldOut runs while the held-out files are not yet present, so "passed the suite but failed held-out" and "failed the suite" stay distinguishable in the report — the overfit signal vs the never-solved signal.
type Price ¶
type Price struct {
InPerM float64 // USD per 1M prompt tokens.
OutPerM float64 // USD per 1M completion tokens.
CacheReadPerM float64 // USD per 1M cache-read prompt tokens.
}
Price is a model's list price in USD per 1,000,000 tokens, split by prompt vs completion (the two bill at different rates). It is the cost axis the DOGFOOD bake-offs kept asking for: capability is only half the picture — "5/5 at what price?" is the other half, and it splits a field that pass-rate alone collapses (gemini-3.1-pro and opus-4.8 both 5/5, but one spends ~4× the tokens).
func (Price) Cost ¶
Cost returns the USD cost of one trial's token usage at this price.
When CacheReadPerM > 0, Usage.CachedTokens (a subset of PromptTokens) are billed at that rate, and only the remainder at InPerM.
When CacheReadPerM == 0, cached tokens are billed at the full prompt rate: Usage.CachedTokens is a subset of PromptTokens that is already counted once, and OpenRouter discounts it — so NOT subtracting it is a deliberate, documented over-estimate (a cost ceiling), never a silent under-count.
type Report ¶
Report is the aggregate of a sweep: the manifest plus every cell's trials. It renders to Markdown (human) and JSON (machine-diffable, for regression diffs).
func Run ¶
Run sweeps every (Case × Model) pair n times, sequentially, and aggregates the trials into a Report. n trials per cell is the point (HP-11): a single run measures the dice — the bake-offs flipped the same model pass↔fail across runs — so the unit of measurement is a distribution, not one boolean. For a real multi-model sweep use RunConcurrent; this is the simple, deterministic path.
func RunConcurrent ¶
func RunConcurrent(ctx context.Context, cases []Case, models []Model, n, workers int, onDone func(Trial)) *Report
RunConcurrent is the same sweep with up to `workers` trials in flight at once. Trials are independent — each materializes its OWN pristine temp fixture and sandbox (trial.go) — so concurrency is safe and the wall-clock of a 30-run roster sweep drops from sum-of-runs to ceil(runs/workers) waves. Results are written into preallocated per-cell slots (distinct indices, no append race), so the report's cell/trial order is identical to the sequential Run regardless of completion order. onDone, if non-nil, is called once per finished trial (from a worker goroutine — it must be concurrency-safe) for live progress.
func (*Report) Markdown ¶
Markdown renders the human-facing report: a manifest header, then one table per case with a row per model.
func (*Report) SweepCost ¶
SweepCost sums the USD cost of every priced trial across the whole report — the bottom-line "what did this run cost". ok=false when nothing was priced.
func (*Report) WriteFiles ¶
WriteFiles archives the report to dir: report.md (human) and report.json (machine-diffable, the input to a future regression diff). The dir is created if absent.
type ReviewFateStats ¶
type ReviewFateStats struct {
Runs int // runs whose report named this reviewer.
Rounds int // total review rounds spent.
Repaired int // blocked, fed back, cleared on a later round.
Refuted int // repro PASSED — the claim was execution-refuted.
Expired int // still blocking when rounds/run ran out.
Notes int // never blocked (severity note / under the confidence gate).
Dropped int // failed deterministic validation (quote/fence).
}
ReviewFateStats aggregates one reviewer model's finding fates.
func (ReviewFateStats) Blockers ¶
func (s ReviewFateStats) Blockers() int
Blockers is how many findings ever stood as (or were escalated toward) blocking: the denominator of the plan's FP formula.
func (ReviewFateStats) FPRate ¶
func (s ReviewFateStats) FPRate() float64
FPRate is the plan's calibration metric: refuted+expired / total blockers (refuted = provably wrong; expired = never converted into a fix — the pessimistic proxy until human labels exist). NaN-free: 0 when no blockers.
type SetupResult ¶
type SetupResult struct {
// LadderVerify, when non-nil, overrides Case.LadderVerify for this trial.
// A pointer distinguishes "no override" from "measured empty; refuse".
LadderVerify *string
}
type TreeEntry ¶ added in v0.2.0
TreeEntry is a git tree entry's full identity. Object alone is insufficient: mode distinguishes executable files and symlinks from regular files.
type Trial ¶
type Trial struct {
Case string
Model string
RunID string // the agent run's stable ID (RunResult.ID) — the P1 spine, so a Trial can be correlated with its persisted transcript.
Index int // 1-based trial number within the cell.
Outcome agent.Outcome // the agent's self-reported terminal state.
Answer string
Iters int
Usage llm.Usage
Pass bool // the ORACLE's verdict — the actual grade.
Detail string // the oracle's evidence.
NoAttempt bool // the oracle found nothing gradable (Grade.NoAttempt) — demoted by best-of selection.
Err string // infra failure (provider/transport), distinct from a non-pass.
Cost float64 // USD cost of this trial's Usage at the pinned Pricing (meaningless unless Priced).
Priced bool // whether the model had a Pricing entry — false => render "—", not $0.
// Plan is the opening plan-stage report (nil when Config.Planner was unset).
// Persisted per trial so planner activity and cost are recorded facts.
Plan *agent.PlanReport `json:"plan,omitempty"`
PlanCost float64 // USD of Plan.Usage at the PLANNER model's pinned Pricing.
PlanPriced bool // false when the planner model has no Pricing entry (or Plan is nil).
// Review is the closing review-gate report (nil when Config.Reviewer was
// unset). Persisted per trial so review activity is a recorded fact, not
// something to infer from outcome flips and billing deltas (the R4b gap).
Review *agent.ReviewReport `json:"review,omitempty"`
ReviewCost float64 // USD of Review.Usage at the REVIEWER model's pinned Pricing.
ReviewPriced bool // false when the reviewer model has no Pricing entry (or Review is nil).
// LatencyMs is the agent's wall-clock for this trial (summed model + tool time
// across steps). Derived from Steps so it survives in report.json after the full
// trace is dropped — lets the report separate slow runs from token-heavy ones.
LatencyMs int64
// Protocol is the loop actually used: "tools", "text", or "refused" (infra
// failure when a tools case met a no-tools model).
Protocol string `json:"protocol"`
// Ladder holds per-trial escalation-ladder metadata when the trial was
// executed by a ladder arm. nil for normal single-model trials — the
// omitempty tag keeps existing report JSON stable.
Ladder *TrialLadder `json:"ladder,omitempty"`
// Steps is the full think->act->observe trace, kept for per-trial archival but
// EXCLUDED from report.json (json:"-") so the aggregate report stays compact —
// WriteFiles writes each trace to its own file under traces/ instead.
Steps []agent.Step `json:"-"`
}
Trial is one execution of a Case by a Model: the agent's typed self-report (Outcome/Answer/Iters/Usage) PLUS the oracle's independent verdict (Pass/ Detail). Keeping both side by side is what makes the false-positive metric computable — a run where Outcome==Answered but Pass==false is a model that claimed success on a task it did not finish (the #1 bake-off failure). It is a flat value (not the full *RunResult) so the report JSON stays compact; the full Step trace is intentionally out of this slice (a follow-up can archive per-trial traces alongside the report).
func RunTrial ¶
RunTrial executes one trial: materialize a PRISTINE fixture into a fresh temp dir (never the live repo, never a reused dir), run the agent against it, then grade the resulting on-disk state with the case's oracle. The temp dir is removed afterward — the fixture is reproducible, and the verdict is what we keep. err-shaped infra failures from the loop are recorded on the Trial (as .Err) rather than returned, so one flaky provider call can't abort a whole suite sweep; the trial just counts as a non-pass.
When m.Run is set (a custom trial runner, e.g. the escalation ladder), RunTrial delegates the solve to that function instead of the normal single-model provider path. m.RequiresVCS gates a pre-run git-baseline initialisation for runners that need a clean work tree.
func (Trial) FalsePositive ¶
FalsePositive reports the run that claimed done but did not actually finish: the agent terminated with Answered, yet the oracle failed it. This is the honesty signal the closing verification gate (VerifyCmd) exists to drive to zero — measuring it here closes the loop on that feature.
type TrialLadder ¶
type TrialLadder struct {
Attempts int // total attempts across all rungs.
WinnerRung int // 1-based rung index of the winning attempt; 0 when nothing won.
Escalated bool // true when any attempt went past rung 1 (the NORTH-STAR guard).
Cost float64 // USD cost of all ladder attempts summed; valid only when Priced is true.
Priced bool // whether at least one attempt had a cost known to the price table.
// CostByModel is the per-model dollar attribution: solver cost under its rung
// model id, reviewer cost under the reviewer model id, planner under the
// planner model id. The sum across entries equals Cost (modulo pricing
// gaps). nil for non-ladder trials (omitempty keeps JSON compact).
CostByModel map[string]float64 `json:"cost_by_model,omitempty"`
}
TrialLadder records the per-trial escalation-ladder metadata for a trial run by a ladder arm. Zero values mean "no info available" — the report renders these as "—".
type TrialRunFunc ¶
type TrialRunFunc func(context.Context, agent.Config, string) (*agent.RunResult, *TrialLadder, error)
TrialRunFunc is an optional custom trial runner. When set on a Model, RunTrial delegates the solve to this function instead of the normal single-model provider path. The runner receives the per-trial agent.Config (already populated with sandbox, root, task, tools) and the case name, and must return a RunResult plus, optionally, ladder metadata. nil ladder => no ladder info.