# Run configuration and verification

Pass `guardrails:` and `verification:` to `BeamAgent.API.run/2` or `start/2`.
Applications can also configure defaults in their own `config/config.exs` or
`config/runtime.exs`:

```elixir
config :beam_agent, :run_defaults,
  guardrails: [max_iterations: 15, max_tool_calls: 20,
               max_execution_time_ms: 60_000],
  verification: [required_tools: [:search]]
```

Precedence is library defaults, application defaults, then per-run overrides.
Guardrails merge by individual key. Verification is replaced as a whole when
provided per run; `verification: []` selects the library default verifier with
no required tools. This prevents options for one verifier leaking into another.
Application `run_defaults` accepts only `guardrails` and `verification`.
Other API options continue to work as before and are outside this resolver.

```elixir
BeamAgent.API.run("Analyze the report",
  llm: {MyApp.ModelClient, []},
  guardrails: [max_iterations: 25],
  verification: [module: MyApp.ReportVerifier, minimum_answer_bytes: 100]
)
```

The library guardrail defaults are:

- `max_iterations: 5`
- `max_tool_calls: 5`
- `max_execution_time_ms: 30_000`
- `max_context_messages: 8`
- `summary_char_limit: 1_000`

All require nonnegative integers. Zero retains its existing runtime meaning:
a zero iteration, tool-call, or execution-time budget blocks that work;
small context limits may fail when required context cannot fit. There is no
`:infinity` option. Unknown guardrail keys, duplicates, and invalid values are
rejected. Defaults are validated even when a run would override them.

Invalid configuration returns `{:error, run}` with
`run.error == {:startup_failed, {:invalid_config, field}}`, where `field` is
`:options`, `:run_defaults`, `:guardrails`, or `:verification`. The Run retains
its generated key. No model or tool is invoked. Existing run telemetry reports
`:startup_failed` without including configuration values. Non-list API options
retain the previous exception behavior.

Configuration is resolved once for each API call, before remote placement.
The remote node receives the resolved options; its `run_defaults` do not
replace them. Changing application defaults affects subsequent calls, not
active runs. Low-level direct Runner/guardrail calls do not load application
defaults; consumers should use the public API.

## Multiple verifiers

Each verifier implements `c:BeamAgent.Verifier.Behaviour.verify/2`. Supply an
ordered nonempty list of `{module, options}` pairs:

```elixir
verification: [
  verifiers: [
    {BeamAgent.Verifier.Default, required_tools: [:search]},
    {MyApp.ReportVerifier, minimum_answer_bytes: 100},
    {MyApp.SourceVerifier, minimum_sources: 2}
  ]
]
```

Every verifier receives the same completed Run and only its own keyword
options. All must return `:ok`. The first `{:error, reason}` stops evaluation;
`run.verification_error` becomes `{:verifier_failed, module, reason}`.
A malformed return becomes `{:invalid_verifier_result, module}`. An exception,
throw, or exit retains the existing verifier-exception/runner-crash behavior.
Private reasons never enter telemetry.

The default verifier is not automatically inserted into a list. Include it
explicitly to require its completed execution, non-nil answer, and required
successful tool checks. A custom single verifier remains supported with
`verification: [module: Module, ...]`. Do not combine `:module` and `:verifiers`
or add shared options beside `:verifiers`; give each module its own options.
BeamAgent checks module availability and the `verify/2` callback. Custom
option semantics remain the custom verifier's responsibility.

These are post-run checks. They do not run at intermediate execution stages,
and execution failures bypass verification. The complete list is one composite
verifier invocation with one verification telemetry span. This preserves the
existing event contract and adds no verifier module names or raw reasons to it.

See [Build an agent](building-agents.md) for complete verifier modules and
application-specific admission/tool checks. Runtime guardrail modules are not
currently injectable; only the documented built-in limits are configurable.
