Section reference
Every section of a Harness or Profile document, with each field's type, default and meaning. Defaults are what applies when no layer sets the field; the framework's defaults profile sets a few of them explicitly (noted below). Unknown keys fail loading, except in blocks that pick a provider, where extra keys are the provider's settings and are validated by the provider.
Types: duration is a string such as 30s, 5m or 1h30m, or a number of seconds (Durations). expression is a cel-lite string. import path is package.module:attribute.
Required in every app spec and profile. A profile's metadata describes the profile and is never merged into the app's.
| Field |
Type |
Default |
Meaning |
name |
string |
required |
Lowercase letters, digits, ., _ and -, starting with a letter or digit |
owner |
string |
none |
The owning team |
risk_tier |
string |
internal |
Picks the required adoption level from the baseline's require_level |
version |
string |
none |
SemVer; required for org profiles, so extends ranges can match it |
description |
string |
none |
Free text |
extends
A list of profile references, merged before this document: profile://rag, org://acme-baseline@^1, profile://acme.baseline@^1, or a file path. See Layers.
plugins
| Field |
Type |
Default |
Meaning |
local |
list of module paths |
[] |
In-repo modules that expose a plugin object ("app.harness_plugin"), for plugins that aren't installed packages |
allow |
list of namespaces |
none (any installed plugin) |
The plugin namespaces this spec may use; usually set and locked by the org baseline. The core's built-in providers are always allowed |
models
Picks the model provider. Extra keys are the provider's settings (Models).
| Field |
Type |
Default |
Meaning |
provider |
name |
none |
The ModelProvider: openai.chat, anthropic.messages, scripted, or a plugin's |
default |
alias |
none |
The alias used when a request names none |
aliases |
map of alias to model |
{} |
What each alias means for this provider (fast: gpt-x-mini). Overlays remap them per environment |
allowed_aliases |
list of aliases |
none (any) |
The aliases runs may request; others are blocked (model_not_allowed). Usually set by the baseline |
prices |
map of model to price |
{} |
{input_per_mtok, output_per_mtok} in USD per million tokens, used when the provider reports no cost |
models:
provider: openai.chat
base_url: http://localhost:4000 # openai.chat's own settings
api_key_env: LITELLM_API_KEY
default: fast
aliases: { fast: fast-chat, strong: strong-chat }
prices:
fast-chat: { input_per_mtok: 0.15, output_per_mtok: 0.60 }
routing
A map of router name to router (Routing).
| Field |
Type |
Default |
Meaning |
options |
list of strings |
required (at least one) |
The answers this router may give |
cascade |
keyed list of stages |
[] |
Stages tried in order; the first confident answer wins |
default |
string |
none |
The answer when no stage is confident; must be one of options. Without one, an undecided router abstains |
Each stage:
| Field |
Type |
Default |
Meaning |
id |
string |
<use>#<n> |
Names the stage in traces and merges |
use |
name |
required |
The Decider: rules, lexical, semantic, llm, or a plugin's |
threshold |
number 0 to 1 |
none |
The lowest confidence a probabilistic stage's answer needs; without one, any confidence wins |
| other keys |
|
|
The stage's settings (stages) |
context
A map of source name to context source (Context and memory).
| Field |
Type |
Default |
Meaning |
use |
name |
required |
The ContextProvider: memory, passthrough, or a plugin's |
label |
untrusted or trusted |
untrusted |
Whether items from this source count as untrusted (RULES S3) |
max_items |
integer > 0 |
none |
Keep at most this many items |
max_tokens |
integer > 0 |
none |
Keep items while their total size fits (one token per four characters) |
require_acl |
boolean |
false |
Also drop items that carry no ACL, not only items whose ACL excludes the run |
| other keys |
|
|
The provider's settings (memory: documents or file) |
memory
The store behind s.memory. Entries are namespaced by tenant, principal and task.
| Field |
Type |
Default |
Meaning |
use |
name |
memory |
The Memory provider; memory keeps entries in process |
default_ttl |
duration |
none (no expiry) |
When entries expire, unless put gives a ttl |
guardrails
Guards per stage. Each stage is a keyed list (Guardrails).
| Stage |
Hook point |
Checks |
input |
input.before |
The run's input, or what s.guard("input", ...) passes |
context |
context.after |
Each retrieved item; a block drops the item |
tool_result |
tool.after |
A tool's output |
output |
output.before |
A candidate final output |
stream |
stream.chunk |
Each streamed text chunk |
Each guard:
| Field |
Type |
Default |
Meaning |
id |
string |
<use>#<n> |
Names the guard (guardrails.input[<id>]) in traces, locks and merges |
use |
name |
required |
The Guardrail provider |
action |
guard action |
the provider's |
Replaces the provider's proposed action whenever it flags something: allow, redact, repair, escalate, require_approval, block, log_only |
mode |
enforce or log_only |
enforce |
log_only records what it would have done and changes nothing (shadow mode, RULES R8) |
where |
local or sidecar |
local |
Run the provider in the app, or in harnessd at sidecar.url |
parallel |
boolean |
false |
Consecutive parallel guards in one band run concurrently |
fail_mode |
closed or open |
closed |
What a raise or timeout becomes; baseline guards must stay closed |
timeout |
duration |
none |
How long the provider may take |
enabled |
boolean |
true |
false switches the guard off (not allowed on a locked guard) |
| other keys |
|
|
The provider's settings |
validation
| Field |
Type |
Default |
Meaning |
max_repairs |
integer >= 0 |
1 |
How many times an invalid output is sent back to the model |
on_failure |
block or escalate |
block |
What happens when the last repair still fails (validation_failed) |
rules |
keyed list |
[] |
Business rules on the output |
Each rule:
| Field |
Type |
Default |
Meaning |
id |
string |
rule#<n> |
Names the rule (validation.rules[<id>]) |
when |
expression |
required |
Over output, input and task. True means the output breaks the rule; an expression that can't be evaluated counts as broken |
message |
string |
required |
Sent to the model with the repair request |
| Field |
Type |
Default |
Meaning |
manifests |
list of globs |
[] |
kind: Tool files, relative to the spec (Tool manifests). A single string is accepted |
require_manifest |
boolean |
true |
No tool runs without a manifest (RULES S2) |
max_output_bytes |
integer > 0 |
20000 |
Cut tool output to this many UTF-8 bytes, unless the manifest says otherwise |
trifecta |
require_approval or block |
require_approval |
A call with egress after untrusted content and private data entered the run (S3) |
approval_by_risk |
map of risk to required or none |
{} |
Risk levels whose calls always need approval: {high: required, critical: required} |
judge |
provider block |
none |
A guardrail that also sees each call ({tool, args}) and may only tighten the decision (governor step 6) |
policy
| Field |
Type |
Default |
Meaning |
use |
name |
cel_lite |
The PolicyEngine: cel_lite, or cel.policy from harnessweaver-cel. Must be deterministic (RULES R3) |
rules |
keyed list |
[] |
The rules |
default_effect |
allow, deny or require_approval |
allow |
The effect for a tool call no rule matches |
Each rule:
| Field |
Type |
Default |
Meaning |
id |
string |
rule#<n> |
Names the rule in decisions and traces |
when |
expression |
required |
True when the rule applies. For tool.before: principal, tenant, task, tool, args, labels. For run.start: principal, tenant, task, labels |
effect |
allow, deny or require_approval |
required |
What the rule decides |
reason |
string |
none |
Shown in the decision |
hook |
tool.before or run.start |
tool.before |
Where the rule applies. run.start rules decide whether a run may start, and only restrict |
See Policy for how rules combine.
approvals
The approval channel and the defaults for every approval. Extra keys are the channel's settings (Approvals).
| Field |
Type |
Default |
Meaning |
use |
name |
memory |
The ApprovalChannel: memory, callback, console, webhook, or a plugin's |
timeout |
duration |
none (the defaults profile sets 24h) |
How long a request stays open |
on_timeout |
escalate or reject |
escalate |
What an answer after expiry does to the run |
approver_role |
string |
none |
The role that may approve, when the step that asked names none |
grant_scope |
call or run |
call |
How long a tool call's approval lasts: one execution, or the rest of the run (Approvals in depth) |
budgets
| Field |
Type |
Default |
Meaning |
per_run.max_usd |
number >= 0 |
none |
Cost limit |
per_run.max_tokens |
integer >= 0 |
none |
Input plus output tokens |
per_run.max_model_calls |
integer >= 0 |
none |
Model calls |
per_run.max_tool_calls |
integer >= 0 |
none |
Tool calls |
per_run.deadline |
duration |
none |
Wall-clock limit for the run |
A task's budget tightens these for that task. See Budgets.
state
| Field |
Type |
Default |
Meaning |
use |
name |
memory |
The StateStore: memory (lost when the process stops) or sqlite |
record |
boolean |
false |
Record each run's model answers, tool results and approvals, for harnessweaver replay. Recordings hold content (RULES R9) |
| other keys |
|
|
The store's settings: sqlite takes path (default .harness/state.db, relative to the working directory) |
observability
| Field |
Type |
Default |
Meaning |
exporter |
none, console or otlp |
none |
Where otel.sdk sends telemetry; needs configure_sdk: true |
endpoint |
URL |
none |
The OTLP endpoint, such as http://otel-collector:4318 |
capture_content |
boolean |
false |
Put prompts, outputs and tool arguments, after redaction, on spans (RULES R9). Usually locked off by the baseline |
configure_sdk |
boolean |
false |
Let otel.sdk (from harnessweaver-otel) set up the OpenTelemetry SDK; leave it off when the app configures OpenTelemetry itself |
service_name |
string |
metadata.name |
The service name on telemetry |
| other keys |
|
|
otel.sdk's settings, such as export_interval |
evals
A map of task name to a list of dataset files, plus the reserved key online (Evals).
evals:
support: [evals/golden.yaml, evals/redteam.yaml]
online: { sample_rate: 0.05, graders: [{ status: completed }] }
online field |
Type |
Default |
Meaning |
sample_rate |
number 0 to 1 |
0 |
The share of production runs graded at run.end, picked by a hash of the run id |
graders |
list of graders |
[] |
Deterministic graders only; online samples have no expect |
middleware
A keyed list of middleware from plugins (Middleware).
| Field |
Type |
Default |
Meaning |
id |
string |
<use>#<n> |
Names the item |
use |
name |
required |
The middleware, as <namespace>.<name> |
band |
baseline, platform, app or task |
the layer's band |
Only profiles may use baseline (RULES S8) |
hooks |
list of hook points |
the middleware's own |
Narrow the hooks it attaches to; it can't attach to hooks it doesn't declare |
fail_mode |
closed or open |
the middleware's own |
Override the declared fail mode; baseline middleware must be closed |
timeout |
duration |
the middleware's own |
Override the declared timeout |
enabled |
boolean |
true |
Switch it off |
| other keys |
|
|
Settings for a class middleware's config model |
tasks
A map of task name to task.
| Field |
Type |
Default |
Meaning |
version |
string |
1.0.0 |
The task's version, on its runs and traces |
description |
string |
none |
Free text |
runner |
name or provider block |
direct |
What executes it: direct, session, workflow, pydantic_ai.agent, or a plugin's. A string is short for {use: <name>} |
input |
import path |
none |
The Pydantic model the input is validated into |
output |
import path, or str |
none |
The type the output is parsed into (str for plain text). Required for L1 |
prompt |
name@version |
none |
The versioned prompt (direct sends it as the system message; pydantic_ai.agent uses it as instructions) |
model |
alias |
models.default |
The model alias for this task |
tools |
list of tool names |
[] |
The tools offered to the model (direct, pydantic_ai.agent). direct blocks a call to any other registered tool (tool_not_offered) |
budget |
budget |
none |
Tightens budgets.per_run, plus overridable (below) |
approvals |
task approvals |
none |
Approval settings for this task (below) |
guardrails |
guardrails |
none |
Guards for this task only, in the task band, same shape as the guardrails section |
budget takes max_usd, max_tokens, max_model_calls, max_tool_calls, deadline and overridable, the list of those fields a caller may lower for one run (Per-run overrides).
approvals:
| Field |
Type |
Default |
Meaning |
tools |
list of tool names |
[] |
Tools that always need approval in this task |
approver_role |
string |
approvals.approver_role |
Who may approve |
timeout |
duration |
approvals.timeout |
How long a request stays open |
on_timeout |
escalate or reject |
approvals.on_timeout |
What expiry does |
Runner settings
The runner block's other keys are the runner's settings.
| Runner |
Setting |
Default |
Meaning |
direct |
max_steps |
8 |
Model calls in the tool loop, not counting repairs. Reaching it blocks the run with max_steps |
session |
handler |
required |
package.module:function called as await fn(session, input) |
workflow |
steps |
required |
The step graph (below) |
workflow |
output |
the last step |
The step whose output is the run's output |
pydantic_ai.agent |
agent |
required |
package.module:attribute of a Pydantic AI agent |
pydantic_ai.agent |
model |
harness |
harness: the spec's models answers; agent: the agent's own model, still governed |
pydantic_ai.agent |
deps |
none |
package.module:function called with (session, input) that returns the agent's deps |
A workflow step:
| Field |
Type |
Default |
Meaning |
id |
string |
required |
Letters, digits, _ and -, starting with a letter |
handler |
import path |
required |
async (session, inputs) -> output; inputs holds the run's input and the outputs of the steps it needs, by id |
needs |
list of step ids |
[] |
Steps that must finish first; steps that are ready together run in parallel |
evaluator |
import path |
none |
async (session, output) -> True, False or feedback text; makes the step an evaluator-optimizer loop |
max_rounds |
integer 1 to 20 |
3 |
How many times the handler may run in the loop |
on_reject |
escalate, block or accept |
escalate |
What happens when the evaluator never accepts |
In the loop the handler gets inputs["feedback"] and inputs["round"]. Finished steps are checkpointed, so after an approval only the interrupted step runs again.
prompts
| Field |
Type |
Default |
Meaning |
dir |
path |
prompts |
Where versioned prompts live, relative to the spec |
Prompt files are named <name>@<version>.md (or .txt), or <name>.md without a version. A reference picks a file:
| Reference |
Picks |
support_answer@4.1.0 |
Exactly that version |
support_answer@4 |
The file named support_answer@4.md, or else the highest 4.x.y |
support_answer |
The highest version, or the unversioned support_answer.md |
Placeholders are {name}, filled with str.format_map semantics; {{ and }} are literal braces. There is no template language; a missing variable is an error. Render one with s.prompt("support_answer@4", question=...), or name it in a task's prompt.
sidecar
| Field |
Type |
Default |
Meaning |
url |
URL |
none |
harnessd, for guards declared where: sidecar |
token_env |
string |
HARNESSD_TOKEN |
The environment variable holding the bearer token |
Profiles only
| Field |
Type |
Default |
Meaning |
require_level |
{default, tiers} |
{default: L0} |
The level an app must reach, by metadata.risk_tier (Adoption levels) |
locks |
list of paths |
[] |
Paths later layers can't change (Locks) |
bounds |
map of path to {min, max} |
{} |
Limits for numbers and durations (Bounds) |