Turn metrics

September 13, 2026 · View on GitHub

OMK records one JSON line per agent turn so harness changes can be measured instead of guessed.

CLI harness SOTA target

OMK targets state-of-the-art quality as a CLI coding-agent harness. SOTA is not verified. This is a product target, not a statement that the current release leads a benchmark or a named competitor cohort.

The target covers the harness layer:

DimensionPrimary measure
Task successsolved tasks and pass rate
Cost efficiencymodel cost and tokens per solved task
Latencywall-clock p50/p95 per solved task
Context efficiencyinput, cache, compaction, and tool-output tokens per solved task
Tool reliabilityfailure, retry, refusal, and intervention rates
Orchestrationcritical-path time, useful concurrency, and duplicate work
Recoveryinterrupted-run resume accuracy and repeated-run variance
Safetypolicy violations, unauthorized effects, and false-positive blocks
Maintainabilitycomplexity, module-size debt, and regression-gate health

No single feature count or self-score establishes leadership. OMK reports a dimension-specific result unless a preregistered aggregation defines an overall score.

Controlled comparison contract

A comparative harness run MUST hold the same model, same provider and model configuration, same task and revision, same budget, equivalent tool permissions, and comparable container, hardware, region, and concurrency constant. The harness is the treatment variable. If a factor cannot be held constant, the report must label the result non-comparative.

Every comparative report must include:

  • the date, harness versions, and named comparison cohort, with its inclusion rule frozen before execution;
  • immutable model, provider, container, environment, and configuration identities;
  • the task manifest, seeds, public prompt or sanitized prompt digest, budgets, run order, and stop policy;
  • sanitized per-task outcomes plus cost, token, latency, retry, and intervention data;
  • the confidence interval, significance threshold, minimum effect, and statistical test chosen before inspecting the result;
  • the exact commands and immutable manifests needed for reproducible evidence.

Use paired A/B measurements. Randomize or interleave pair order when provider or machine drift can bias one side. Do not combine values produced by different methods, working directories, task revisions, or warm/cold conditions. A roadmap projection remains a hypothesis even when its inputs are measured.

Evidence privacy

Benchmark evidence is private by default. Public artifacts may contain public or synthetic task identifiers, version and image digests, allowlisted runtime metadata, sanitized outcomes, and aggregate statistics. They must not contain credentials, private prompts, proprietary source, raw tool arguments or output, environment values, personal data, or absolute user paths.

Keep restricted raw evidence local or in an access-controlled store with an explicit owner and retention period. Normalize paths, redact content, scan for secrets and PII, and obtain human approval before publication. Digests and public task manifests preserve reproducibility without disclosing restricted content.

A public “SOTA,” “best,” “leading,” or “#1” claim requires a dated controlled comparison that places OMK on the relevant quality/cost/latency frontier without violating declared safety and regression floors. Until then, use “targets state-of-the-art quality.”

Harbor's Terminal-Bench runner and the SWE-bench containerized harness are reference evaluation surfaces. Their presence or a selected task list is infrastructure, not a benchmark result.

This is separate from the two things that already existed:

SurfacePurpose
core/run-journal.tsHash-chained integrity log (run started/finished/recovered, tool timeout). Answers "was this run tampered with or abandoned".
core/telemetry.tsInstall-time opt-in flag. Nothing else.
core/turn-metrics.tsPerformance and quality: cost, latency, tool failure rates, cache effectiveness.

Where it goes

<cwd>/.omk/metrics/turns.jsonl, append-only, rotated once past 8 MB (turns.jsonl.1), file mode 600.

VariableEffect
OMK_TURN_METRICS=0Disable recording entirely.
OMK_TURN_METRICS_DIRWrite somewhere else.

Reading it

omk stats                    # aggregate report for the current project
omk stats --dir <path>       # a different metrics directory
omk stats --json             # machine-readable summary
Turn metrics — 412 turns across 27 session(s)
  models        anthropic/claude-sonnet-4-5
  turn duration p50 4.2s · p95 31.8s
  input 91,204 · output 22,880 · cacheRead 1,904,551 · cacheWrite 88,100
  cache read share 95.4% of prompt-side usage
  cost          \$4.8812
  compactions 6 · failovers 1 · ctx plan hit 41.2%

  tool                 calls   fail%     p50     p95    total
  bash                   688    4.2%   210ms    3.1s     4.1m
  edit                   201    9.0%    38ms   140ms    12.4s

What is recorded

Counts, durations, ids, and error classes:

{
  "schemaVersion": "omk-turn-metrics-2",
  "sessionId": "…", "turnIndex": 12,
  "provider": "anthropic", "model": "claude-sonnet-4-5",
  "startedAtEpochMs": 1, "endedAtEpochMs": 2, "durationMs": 1,
  "usage": { "input": 100, "output": 20, "cacheRead": 900, "cacheWrite": 10, "costUsd": 0.0125 },
  "stopReason": "toolUse",
  "toolCalls": [{ "name": "bash", "durationMs": 120, "ok": false, "errorClass": "unknown" }],
  "toolCallCount": 1, "toolFailureCount": 1
}

New records project an explicit field allowlist. Prompt, argument, output, environment, and unknown nested fields are not copied into the record; caller-supplied toJSON properties are not retained. Raw tool errors are classified as timeout, aborted, permission, not_found, invalid_input, or unknown and then discarded. Length truncation alone was not redaction. Identifiers are still metadata, not anonymized identities: callers must not put secrets or task content in ID/name fields.

The reader validates required fields, finite nonnegative quantities, counters, and nested tool/usage/cache shapes before aggregation. Invalid records count as malformed. Valid v1 records remain readable, but existing files are not rewritten or scrubbed; review old files separately before sharing them.

Metrics remain advisory. Invalid input, an oversized record, or a failed write is counted and dropped rather than failing the agent. Single records cannot exceed the configured file bound. Rotation is still best-effort and does not provide a transactional multi-writer size guarantee.

Capability baseline

Runtime metrics tell you what a session cost, not whether the harness can solve tasks. For that, scripts/tb-mini-suite.mjs selects a deterministic, difficulty-stratified Terminal-Bench 2.1 subset. Score comparisons still require the controlled comparison contract above:

node scripts/tb-mini-suite.mjs             # human-readable selection
node scripts/tb-mini-suite.mjs --json      # feed a runner
node scripts/tb-mini-suite.mjs --seed 7    # a different fixed subset

With identical task metadata, selection version, seed, size, and collation, selection is repeatable. The default 15-task subset oversamples easy tasks and prioritizes shorter expert time estimates; it is a regression signal, not a population-representative score or an agent runtime bound. Selection alone is not a capability result. Scoring requires Docker, harbor, and model spend, and is not wired into npm run check.

--size must be a positive safe integer no larger than the available task population. Missing difficulty quotas are filled from unselected tasks using the same ordering, so a valid request returns exactly that many distinct tasks. --seed accepts integers from 0 through 4294967295. Invalid or missing option values and oversized requests exit with code 2; absent, empty, or non-directory task paths exit with code 1.

The JSON output now declares selectionVersion: 2. Missing, empty, nonfinite, or negative expert-time estimates are null, not zero. Within each difficulty band and in quota refill, known estimates sort before unknown estimates. A genuine zero or fractional estimate remains valid. Difficulty quotas still take precedence, so unknown-estimate tasks can be selected to fill a band.

knownExpertMinutes sums known estimates; unknownExpertEstimates counts selected tasks with unknown estimates. totalExpertMinutes is null when any selected estimate is unknown. A nonfinite sum is refused with exit 1, not serialized as an apparently missing total. Expert estimates are not agent timeout limits.

For sizes 1–2, available slots go first to the highest-weight difficulty bands (medium, then hard), with normal refill if those bands are unavailable. Task names no longer decide which excess band quota is discarded. The normal-size quota rule is preserved. Human-readable counts use a Map, including for labels such as __proto__ that overlap JavaScript object properties.

This is a selection and output-contract change: default membership can change when metadata is incomplete, and the prior default JSON digest is historical only. Freeze new task manifests before comparison; do not combine version 1 and 2 runs as if their selection policy were identical. The limited flat TOML field reader is unchanged; this does not add general TOML syntax support.

Run the offline CLI regression tests without downloading tasks or calling models:

node --test --test-concurrency=1 scripts/test/tb-mini-suite.test.mjs scripts/test/tb-mini-suite-ranking.test.mjs

See the harness roadmap for the dated OMK versus Terminus-2 baseline, statistical limitations, implementation boundaries, and staged acceptance criteria. Planned runtime improvements are not measured benchmark gains.

Audit recorded TB 2.1 results

The checkout-only scripts/tb21-audit.mjs audits explicitly selected Harbor jobs against a caller-pinned manifest digest. It rejects duplicate tasks/trials, missing results or costs, mismatched task checksums or configured model labels, missing/invalid completion times, and contradictory success records. It never starts a model, picks the latest job, joins requests by timestamp, or rewrites evidence. See TB 2.1 offline audit for the schema, invocation, error codes, and limitations.

A complete audit means recorded outcomes passed these checks, not that every provider request obeyed a single-model contract. Wire provenance, actual billing, repeated-trial analysis, and statistical superiority need separate evidence.

Output-limit validation: availability history

2026-09-13 source snapshot (ca75f4e5cc): the logical contract, CLI/SDK wiring, and final Chat Completions model/output-limit checks are in the committed source. Model dispatch contracts defines their coverage; Verified Run describes the separate protected execution path. Neither is a universal provider billing cap or a new controlled benchmark result. See the roadmap, section 16 for current local release-preparation checks.

The following paragraphs describe the earlier checkout only. Its missing modules and failing collection are historical observations, not active release blockers.

2026-09-08 follow-up: the current worktree restores the logical contract and connects it through the CLI/SDK, including SDK-stream summaries. See Model dispatch contracts and ROADMAP §13 for fresh evidence and the remaining final-wire/accounting gaps. The warning below records the preceding checkout, not the current availability of the restored module.

The previous worktree checkpoint tested positive-safe-integer validation of modelContract.maxOutputTokens and explicit request maxTokens. During the 2026-09-08 re-verification, the checkout changed: run-model-contract.ts and the corresponding AgentLoopConfig.modelContract surface were absent. The remaining model-contract-output-limit.test.ts fails collection against that checkout.

Do not treat the historical passing tests as proof that this guard is currently available. Restoring or porting the runtime contract requires an explicit source baseline decision and fresh send-boundary tests. No missing code was silently recreated and no failing test was deleted. Full run-wide enforcement, including omitted limits and compaction, remains unverified; see ROADMAP sections 11–12.