# Agent Harness API
`fharness` is the top-level orchestration layer for long-running agent workflows in Fiddlesticks.
It currently supports:
- initializer flow
- task agent incremental loop
- runtime wiring + run-level policy
- reliability + guardrails for production usage
`fharness` composes lower layers (`fmemory`, `fchat`, `ftooling`, `fprovider`) into a structured multi-run harness.
## Responsibilities
- Run initializer setup for a session (manifest + feature list + progress + checkpoint)
- Run incremental task iterations one feature at a time
- Enforce clean handoff by recording explicit run outcomes
- Coordinate health checks, execution, validation, and persistence updates
`fharness` does **not**:
- Implement provider transports (`fprovider`)
- Implement turn orchestration internals (`fchat`)
- Implement tool runtimes (`ftooling`)
- Implement persistence internals (`fmemory`)
## Add dependency
```toml
[dependencies]
fharness = { path = "../fharness" }
fmemory = { path = "../fmemory" }
fchat = { path = "../fchat" }
```
## Core types
- `Harness`: orchestrator for initializer and task iterations
- `HarnessBuilder`: runtime wiring for provider/chat/tooling/memory
- `InitializerRequest` / `InitializerResult`
- `TaskIterationRequest` / `TaskIterationResult`
- `RuntimeRunRequest` / `RuntimeRunOutcome`
- `HealthChecker` (`NoopHealthChecker` default)
- `OutcomeValidator` (`AcceptAllValidator` default)
- `FeatureSelector` (`FirstPendingFeatureSelector` default)
- `HarnessError` / `HarnessErrorKind`
## Initializer flow
`run_initializer(...)`:
1. Validates objective and feature list rules
2. Builds/versions `SessionManifest`
3. Uses `initialize_session_if_missing(...)` for idempotent initialization
4. Persists initial progress + run checkpoint
If no feature list is provided, `Harness` generates a starter skeleton via `starter_feature_list(...)`.
```rust
use std::sync::Arc;
use fharness::{Harness, InitializerRequest};
use fmemory::{InMemoryMemoryBackend, MemoryBackend};
let memory: Arc<dyn MemoryBackend> = Arc::new(InMemoryMemoryBackend::new());
let harness = Harness::new(memory);
let request = InitializerRequest::new("session-1", "run-init-1", "Build incremental harness");
let _result = harness.run_initializer(request).await?;
```
## Task-Iteration incremental loop
`run_task_iteration(...)` executes one bounded run:
1. **Get bearings**
- loads manifest/progress/features from `fmemory`
- runs health check using manifest `init_plan`
2. Picks one highest-priority failing feature (`passes == false`)
3. Delegates execution to `fchat` (`run_turn` or `stream_turn`)
4. Validates outcome via `OutcomeValidator`
5. Updates artifacts:
- marks feature passing only when validated
- appends progress entry
- records completed checkpoint with status/note
## Integrated runtime and policy ownership
`HarnessBuilder` wires lower-layer runtime dependencies directly:
- provider (`fprovider`)
- chat service (`fchat`) with transcript storage from `fmemory`
- tool runtime (`ftooling`)
- memory backend (`fmemory`)
`fchat` stays responsible for turn orchestration. `fharness` owns run-level policy:
- phase selection (`initializer` vs `task-iteration`)
- feature selection strategy (`FeatureSelector`)
- validation gate before marking feature pass (`OutcomeValidator`)
## Reliability + guardrails
Harness run policy now supports reliability constraints:
- `mode` (`strict_incremental`, `bounded_batch`, `unlimited_batch`)
- `max_turns_per_run`
- `max_features_per_run` (used by strict/bounded modes)
- `retry_budget`
- fail-fast conditions (`health check`, `chat`, `validation`)
Mode semantics:
- `strict_incremental`: exactly one feature per run (`max_features_per_run` must be `1`)
- `bounded_batch`: process up to `max_features_per_run` features in one run
- `unlimited_batch`: process features until completion gate or turn/retry constraints stop progress
Completion guardrail:
- harness does not declare done early
- completion requires all required features in `feature_list` to have `passes = true`
```rust
use std::sync::Arc;
use fharness::{Harness, RuntimeRunRequest, RuntimeRunOutcome};
use fmemory::{InMemoryMemoryBackend, MemoryBackend};
let memory: Arc<dyn MemoryBackend> = Arc::new(InMemoryMemoryBackend::new());
let harness = Harness::builder(memory)
.provider(provider)
.tool_runtime(tool_runtime)
.build()?;
match harness.run(RuntimeRunRequest::new(session, "run-1", "Build feature loop")).await? {
RuntimeRunOutcome::Initializer(_) => {
// session bootstrapped
}
RuntimeRunOutcome::TaskIteration(result) => {
// one task iteration completed
assert!(result.validated);
}
}
```
```rust
use std::sync::Arc;
use fharness::{Harness, TaskIterationRequest};
let harness = Harness::new(memory).with_chat(chat_service);
let request = TaskIterationRequest::new(session, "run-task-1");
let _result = harness.run_task_iteration(request).await?;
```
## Extensibility hooks
- `HealthChecker`
- run startup/baseline checks before task-iteration work
- `OutcomeValidator`
- enforce real validation gates before marking features as passing
Use `with_health_checker(...)` and `with_validator(...)` to override defaults.
## Clean handoff guarantees
Every task-iteration run records terminal outcome artifacts:
- run checkpoint with explicit `status` (`Succeeded`/`Failed`) and note
- progress entry summarizing what happened
This avoids ambiguous handoff state across context windows.
## Error model
`HarnessErrorKind` variants:
- `InvalidRequest`
- `Memory`
- `Chat`
- `Validation`
- `HealthCheck`
- `NotReady`