Harness reference
Every knob of a run, layer by layer. How to use them is on Harness tuning.
On gcp-0, 2026-10-18: harness v0.15.2, composed by crossplane-configuration v0.9.15.
| Status | Meaning |
|---|---|
live | Seen working on a real run |
deployed | Configured, not yet exercised |
off | Shipped, switched off |
design | Specified, not built |
Factory: tiers
config.tiers in tooling/base/agent-factory/helm-values-configmap.yaml. Triage fixes one tier per
task; every run of the task inherits it.
| Tier | runUSD / taskUSD | runMinutes | runTokens / taskTokens |
|---|---|---|---|
| light | 0.51 / 1.02 | 20 | 300 k / 600 k |
| standard | 2.55 / 5.10 | 45 | 1.5 M / 3 M |
| frontier | 6.80 / 13.60 | 90 | 4 M / 8 M |
| Field | What it does | Bounds |
|---|---|---|
model | The logical model the run asks for. Every tier asks for agent-default today, so the tier changes caps, not the model | A logical name |
runUSD | The meter revokes the run at this cost (BudgetExhausted, limit run-usd). Cache reads are priced at their own rate | Positive; needs a meter.prices row |
runTokens | The claim’s budget.maxTokens. Caps the run only when the meter cannot price it | 1 … 5 M |
taskUSD, taskTokens | The whole task, every run and resume. A new run starts only if a whole run still fits | ≥ the run cap |
runMinutes | The pod’s deadline. A kill at the deadline is PodFailed and never resumed | 1 … 480 |
A priced run is held to USD, not tokens: kk2egjta passed 12.5 M tokens against a 4 M maxTokens
at $4.46 of $6.80, since 95% were cache reads. The gateway’s per-run budget B1 is the backstop
(gateways).
Factory: templates and triage
config.templates: the roles that run, in order, on one branch.
| Template | Roles | Review rounds |
|---|---|---|
solo | implementer | — |
pair | implementer, reviewer | 2 |
trio | implementer, tester, reviewer | 2 |
investigate | triager: proposes an issue text, never writes | — |
maxReviewRounds: every revision or restarted review uses one. Out of rounds, the task escalates.reviewerModel: the reviewer’s model whatever the tier says. Unset here: ADR-0058 keeps the public reviewer on the implementer’s model.
Triage picks the template from the class and the tier:
| Task | Template |
|---|---|
| A RunLore finding | investigate, forced internal |
docs-links | solo; pair on frontier |
| Any other class | pair; trio on frontier |
The tier would come from the complexity classifier, which is
standard, except one in ten (triage.controlPercent: 10), the control group, on frontier.
defaults.template and defaults.tier are read by nothing the deployed factory runs.The reviewer runs on the other tier: frontier for a standard or light implementer,
standard for a frontier one. It keeps the implementer’s maxTokens. The tester keeps the
implementer’s tier.
Factory: caps, resume and suspend
| Key | Value here | What it does |
|---|---|---|
caps.activeTasks | 3 | Tasks worked at once |
caps.concurrentRuns | 4 | Sandboxes at once |
caps.tasksPerDay | 20 | Past it, a labelled issue is refused with a comment |
caps.maxTextBytes | 14,336 | The longest task text accepted |
caps.maxPendingMinutes | 30 (5 … 480) | A run still Pending escalates as run_unschedulable |
resume.maxPerTask | 2 (1 … 5) | Runs lost to infrastructure are resumed; never a PodFailed one.live |
sandbox.suspend.enabled | true on gcp-0 | An attended run waiting on an approval gives up its pod after 5 min. live |
budgets.* | factoryDaily 25 M, humanDaily 5 M | Daily caps; the per-person one counts in shadow |
Run API: runs started by hand
task agent:run -- --role … --class … --task … asks the factory for one run.
| Flag | Default | Note |
|---|---|---|
--max-tokens | 2,000,000 (≤ 5 M) | A hand-started run has no tier, so it is capped in tokens |
--model | agent-default | One of the four logical names |
--profiles | none | Egress profiles: pypi, npm, golang, crates |
--minutes, --size | refused | Fixed at 120 min and small |
AgentRun claim
The XRD agentruns.cloud.ogenki.io
(definition).
Only budget.maxTokens can change on a live run. The factory always writes size: small.
| Field | Default | Effect |
|---|---|---|
role | required | implementer writes and pushes; reviewer, tester, triager read only |
dataClass | required | public: Z.ai, GitHub egress. internal: Claude, read-only clone, no egress, cluster read tools, prompt caching |
model | agent-default | The harness’s MODEL |
budget.maxTokens | 2 M (1 … 5 M) | The token cap when the run is not priced |
budget.maxMinutes | 120 (1 … 480) | The pod’s deadline and the TTL of its tokens |
size | small | small 1→2 CPU, 2→4 GiB; medium ×2; large ×4 |
egress.profiles | [] | Package registries on top of GitHub; refused on internal |
roomRef | — | Adds the room-bridge sidecar and the room rules |
suspendable | — | /workspace on a PVC, so the run can be suspended |
Composition
apis/agentrun/kcl/main.k
turns the claim into the pod. Nothing here is set per run.
| Setting | Value |
|---|---|
| Harness image | ghcr.io/smana/agent-harness:v0.15.2@sha256:c49c…: a harness change reaches runs only through this digest |
| Environment | MODEL, DATA_CLASS, ROLE, LLM_BASE_URL, MCP_URL, TASK_FILE, RULES_FILE, tracing. LLM_REASONING_EFFORT is not set |
rules.md | One branch, one PR, never .github/workflows/, “a 429 means the budget is spent: stop”. Each rule is also enforced outside the sandbox |
task.md | The task text: the biggest driver of a run’s length |
| Egress | github.com and its API, codeload and content hosts, plus each profile’s registries |
| Grace period | 30 s, 45 s with a room: checkpoint, final read, stop, revoke |
| Read-only MCP tools | Flux, VictoriaMetrics and VictoriaLogs reads and the room_* tools; any other needs an approval in a room |
Harness
Set in agent_run.py
and sitecustomize.py,
or left at the SDK v1.49.6 defaults.
None is a claim field.
| Knob | Value | Effect | Status |
|---|---|---|---|
reasoning_effort | high | Unsent, GLM-5.3 thinks at maximum: 150 s per reply instead of 11 s | live |
| Model timeout | 300 s per call | A timeout is not retried: it ends the run | live |
| Retries | 3 attempts, 10 s then 20 s apart | On a 503 or a reset. Never a 429 or a timeout | live |
max_iterations | 500 | Past it the run ends error; the USD cap and deadline usually end it first | deployed |
| Stuck detection | last 20 events | Ends the run stuck on 4 identical cycles, 3 repeated errors or 3 messages with no action | live |
| Condenser | 80 events, keep first 4 | Summarises history with the same model, on the run’s budget | deployed |
| Event size | 30,000 chars | A huge output is truncated, not refused | deployed |
| Tools | terminal, file editor, task tracker, MCP | No browser; MCP tools filtered per role by the gateway | live |
| Prompt caching | on for internal | 85% of input from cache: $0.48 instead of $2.13 | live |
autotitle | off | Saves a model call per title | live |
| Step log | every 15 s | Lines arrive in batches | live |
| Progress notes | 5 milestones, ≤ 8 a run | Fixed texts in the room, nothing the model wrote | live |
| Checkpoint on SIGTERM | implementer only | A reclaimed run’s work survives on its branch | live |
| Tracing | span per model and MCP call | No prompt or output content | live |
# One run's tokens by model and type, as the meter prices them
sum by (gen_ai_request_model, gen_ai_token_type) (
max_over_time(gen_ai_client_token_usage_sum{ar_agent="system:serviceaccount:agents:xplane-run-<id>"}[1d]))