-
Notifications
You must be signed in to change notification settings - Fork 0
Harness
The harness is the machinery between "the agent decides to run a graph" and "the graph finishes": one authoring boundary, one scheduler, one event-sourced store, and the contracts that keep every party honest. This page walks it in lifecycle order.
-
/dag-flow <task>— the conversational entry. The routing guidance picks a shape for the task: plain conversation, onetaskchild, or a workflow graph. -
The
workflowtool — eleven actions: start / extend / control (pause · resume · cancel · step · complete · replan) / status / result / list / read / guide / draft / validate. -
draft— pass a structured graph as tool parameters; the harness renders the YAML spec, validates it, and returns thespec_path. Field-name mistakes are rejected by the tool schema at the provider boundary — they never reach the file.
One contract underneath all three: graph content lives in files, tool parameters carry only paths. A nested graph passed inline would get stringified by provider tool-call serialization — that failure mode is why the boundary is shaped this way.
The single authority from source to prepared graph. Pipeline: parse the YAML → strict action decode → compile blocks into nodes (a review block expands into three: standards review, intent review, arbitration) → profile checks. The portable profile validates structure without touching your environment; environment additionally resolves agents, prompt assets, and models against the live catalogs. No valid prepared graph means zero events and zero persistence — a malformed graph never starts "just to see".
- The dependency graph is layered into waves; a wave runs in parallel up to the concurrency semaphore (
max_concurrency, default 5). - A node is durably
queuedat admission; the child session is only created once a concurrency permit is held. A 100-node fan-out never materializes 100 sessions at once. - Every node is a real child session — same code path as the
tasktool, with its own agent, context window, and tools. - Model resolution per node: tier (
dag.jsonc) → worker agent model → parent session model. If nothing resolves, the workflow is not created; you are asked to configure. No silent defaults. - Ownership: the directory stamp on the workflow row is re-read from the database on every check; adoption holds a conditional claim (one UPDATE that succeeds only while the row exists and is non-terminal); a moved session re-stamps its workflows in the same transaction.
Every state change is a durable dag.* event. The projector writes the SQLite read model inside the publish transaction, so the read model cannot lag the log. History is event replay; there is no log table. Status transitions go through a declared state machine — invalid transitions are typed errors (HTTP 409, not 500). Terminal states are irreversible, with one sanctioned exception: extend may reopen a workflow that completed naturally at a reporting checkpoint, because a reporting leaf means a parent is awake and listening.
Ephemeral summary events (progress counts) are deliberately excluded from the durable manifest — they are recomputed, never replayed.
-
The parent never polls. Wake-ups are synthetic messages delivered when a
report_to_parentnode reaches a terminal state, or when the workflow itself terminalizes. - Dependency outputs flow automatically: a node's
depends_onoutputs become template variables and structured context in its prompt. - Outputs come in three shapes: plain text; structured (
output_schema+submit_result); or a file — a final reply that is a single absolute path is captured as{content_ref, size, sha256, summary}, andresultreturns the pointer. -
resultreads in bounded pages with a cursor.
- A review block compiles into two independent reviews plus an arbitration;
REJECTis a real outcome. In one real run, a fix train was rejected because a hardening commit had no falsifiable probe — the fix loop added the probe, re-ran, and passed. - Verification discipline: red-first probes and mutation re-proof. The regression test must fail before the fix and pass after; reverting the fix in a scratch workspace must turn it red again, and the restore must be byte-identical.
- Deep-mode admission: a bounded Q&A round produces a fingerprinted Requirement Brief; the verdict is
READY/NOT_READY/WAIVED, and a material change invalidates the fingerprint. - Review fingerprint binding: change the implementation and the fingerprint changes — a stale
ACCEPTcannot satisfy the gate.
Replans mark superseded nodes and bump the workflow's graph_rev. Views and terminal aggregation read the current revision only: a superseded failure never fails a workflow that succeeded on its replacement, while a live failure on the current graph stays visible until it is actually fixed. Durable history is not deleted — an agent can audit superseded nodes by id through the result store; the TUI exposes no entry to it.
Four knobs: max_concurrency (5), max_node_replan_attempts (5), max_total_nodes (100), per-node timeout_ms (10 minutes, queue wait included). A deadline breach escalates: the orchestrator is woken to adjudicate (extend the deadline or cancel), and a node that keeps exceeding caps is force-cancelled.
Crash recovery is lazy, per-workflow, evidence-based. Finished sessions back-fill their captured output; ambiguous ones pause the workflow and hand disposition to the parent; provider work is never replayed — a half-finished model call is not re-sent behind your back. Failures carry a class (timeout / exec_failed / verdict_fail) that determines the repair: longer deadline, config fix, or contract restatement.
Implementation detail for each layer lives in Engine Internals.