Harness Flow

Configuration

All model parameters can be configured in model_config.dart and all harness parameters can be configured in harness_config.dart.

Project Structure & Terminology

All agent code lives under packages/frontend/lib/features/agent/.

Folder Overview

FolderPurpose
config/Static constants (HarnessConfig, ModelConfig) — run-loop limits, timeouts, model name, thinking budget.
data/Repository interfaces (AgentChatRepository) and their implementations (GenkitAgentChatRepository), plus the conversation model abstraction.
logic/coordination/Coordinators — stateful classes that serialize, deduplicate, and orchestrate async workspace operations. See below.
logic/policy/Policies — mostly stateless rules and evaluation logic (validation prompts, path sandboxing, runtime observation evaluation).
logic/run/Run lifecycle — the turn loop (AgentRunController), session state (AgentSession), system prompt, and bootstrap context builder.
models/Immutable value types, enums, and extensions shared across layers (AgentAutomationResult, AgentToolEvent, AgentValidationResult, etc.).
tools/Tool definitions: the AgentToolRegistry that builds Genkit tools, tool I/O schemas, and individual tool implementations grouped into search/ and workspace/.
view_models/UI-facing view models (AgentChatViewModel).
views/Flutter widgets for the agent chat panel, message bubbles, tool call rows, etc.

Coordinator Hierarchy

The coordination/ folder contains one central coordinator and three sub-coordinators:

AgentHarnessCoordinator          ← central: orchestrates validation gate sequence
  ├── AgentPubGetCoordinator     ← sub: serializes/deduplicates `flutter pub get`
  ├── AgentDiagnosticsCoordinator← sub: bridges mutation generations with LSP diagnostics
  └── AgentPreviewCoordinator    ← sub: idempotent preview start/reload/rebuild

The sub-coordinators don't know about each other. Only the AgentHarnessCoordinator knows all three and runs them in the correct order (dependency → analyzer → preview → runtime).

Key Terminology

TermMeaning
RunOne user message → agent processes it (potentially multiple model turns) → result. Managed by AgentRunController.
Model TurnOne call to generateTurn() producing new messages. A run can have many turns (tool calls, repairs).
Validation GateA checkpoint before the agent's final response is accepted. Gates run in order: dependency resolution, analyzer, preview load, runtime observation, preview start.
Repair AttemptWhen a validation gate fails, the harness injects a repair prompt and gives the model another chance. Bounded by maxRepairAttempts.
Mutation GenerationA monotonically increasing counter that tracks workspace changes (pubspec edits, Dart file writes). Used by coordinators to deduplicate and sequence work.
CoordinatorA stateful class that serializes async operations, deduplicates in-flight work by generation, and tracks pending/completed state.
PolicyA (mostly) stateless class that encapsulates decision rules — e.g. whether a diagnostic is blocking, how to format a repair prompt, whether a file path is allowed.

Flow until final candidate

The harness drives the model through a bounded validation loop. It provides tools for the model, environment observations and enforces validation. The model is free to generate tool calls and edits for up to maxModelTurnsPerRun turns. A turn is one call to generateTurn(...) that produces new messages. One turn can have tool calls, tool responses, and a final plain-text answer. When the model produces a final answer, the harness checks if code was modified. If the code was modified, the harness pipes messages from the analyzer to the model so it can validate the code. If problems are reported from the analyzer, the harness feeds a focused repair prompt to the model. To not get stuck in a loop the model is only allowed to make maxRepairAttempts repair attempts before the harness gives up and fails the run. Context grows incrementally — after each completed tool roundtrip and validation result, messages are appended directly to the session's model context. There is no separate batch-commit step. Changes to the pubspec.yaml are treated as a special case and trigger an immediate async pub get call while the agent can modify other files in the meantime. The harness waits for the pub get to complete before it sends the analyzer messages to the model for validation. After a successful validation the harness enters the next phase where it loads the code changes into the preview and observes for runtime errors.

Preview Observation and Runtime Error Detection

After the analyzer passes, the harness loads pending code changes into the running preview and actively monitors for runtime errors. This happens inside validateCandidateFinal() as part of the same validation pipeline — analyzer errors, compile errors, and runtime errors all count against the same `maxRepairAttempts budget.

Preview operation lifecycle

Every preview operation (start, rebuild, hot reload) follows the same three-phase sequence inside AgentPreviewAutomation._runPreviewOperation()

  1. Serialization gatewaitUntilAvailable() polls isBusy at 100ms intervals for up to 10 seconds. This prevents concurrent preview operations from racing (e.g. a rebuild still running when the next validation triggers a hot reload). If the timeout expires, the operation fails with a “preview is currently busy” error.

  2. Await the operationawait operation(preview) runs the actual start/rebuild/hot reload. This has no timeout; the harness waits as long as the compiler needs. Compile errors surface as exceptions and are caught and reported as blocking preview errors.

  3. Runtime observation window — After the operation completes successfully, AgentPreviewRuntimeEvaluator.waitForNewBlockingObservation() checks for runtime errors. It first checks immediately (the error may already be present), then waits previewObservationWindow (currently 1s) and checks again. This gives the Flutter framework time to build and lay out the widget tree, which is when runtime errors like RenderFlex overflowed occur.

Flowchart

Yes No Yes No No Yes Yes No Yes Yes No No Yes No Yes No Yes Yes No No Yes No User request AgentSession.appendUserMessage() First user message in session? Append workspace tree bootstrap context AgentRunController loop (maxModelTurnsPerRun) conversationModel.generateTurn(...) session.appendMessages(newMessages) Tool requests returned? Execute requested tools session.appendMessages(toolResponse) Harness coordinator records mutations, pubspec changes, pending preview work Non-empty final text? Fail run Run validation gates ensureDependencyResolution() Dependency blocker? Inject focused repair prompt Unvalidated mutations? Read fresh diagnostics snapshot Analyzer blocker? Load pending preview generation if preview is running Preview load/rebuild/hot reload blocker? Check runtime observations Runtime blocker? Validated mutations and preview not running? Start preview Preview start blocker? session.finishRun(assistantMessage) Run complete Repair attempts left?