Sections
On this page
How to use a Harness
Build and publish a version, start a run, connect an external agent, and route an approved result into Flownix.
Assemble a version
Look for an existing definition before creating one. workspace_id is the Flownix project_id.
harness.list({ workspace_id })
harness.create({ workspace_id, name: "architecture-review", description: "Evaluate architecture options", type: "review_board" })
harness.create_version({ harness_id, changelog: "initial version", input_schema: { type: "object" }, default_budget: { max_run_duration_seconds: 3600, max_tokens: 200000 } })
harness.add_role({ harness_version_id, key: "reviewer", name: "Reviewer", objective: "Return a supported verdict", output_schema: { type: "object", required: ["summary"] } })
harness.add_workflow_node({ harness_version_id, type: "review", name: "review", config: { role_key: "reviewer", lease_seconds: 900 }, retry_policy: { max_attempts: 2 }, timeout_seconds: 1800 })
harness.add_workflow_node({ harness_version_id, type: "end", name: "done" })
harness.connect_workflow_nodes({ harness_version_id, from_node_id, to_node_id })
harness.set_context_policy({ harness_version_id, include: ["run_input", "role", "predecessors"], exclude: ["flow_nodes"], max_tokens: 16000 })
harness.set_output_schema({ harness_version_id, output_schema: { type: "object", required: ["summary"] } })
harness.validate({ harness_version_id })
harness.publish({ harness_version_id })Output schemas are enforced. A role's output_schema (JSON Schema 2020-12) is checked on every work.complete of that role, and the version's output_schema on steps that lead into end: a mismatch is refused with output.schema_violation, the lease stays with the agent, and it retries with a fixed output. A schema that is not valid JSON Schema is refused with output.schema_invalid and blocks publish.
harness.set_context_policy decides what work.get_context returns: include sections run_input, role, predecessors, all_steps; exclude top-level output keys; max_tokens caps step outputs (JSON bytes / 4, earliest dropped first, reported as context_truncated). Without a policy agents get run_input, role, and predecessors.
A published version is immutable. Build changes in a new empty draft version, or use harness.clone to copy a complete definition under a new name.
Branches, loops, and councils
Conditional edges evaluate the source step output. Branch on an explicit outcome:
harness.connect_workflow_nodes({ harness_version_id, from_node_id: review_node_id, to_node_id: approved_node_id, condition: { field: "outcome", op: "eq", value: "approved" } })condition, loop, create_flow_nodes, and end run without an agent. A loop uses config.max_iterations, optional config.exit_condition, and outgoing continue / exit edges. parallel_group and transform are accepted but not executed; use fan-out edges instead.
For a council, give its debate, vote, and judge nodes the same config.deliberation_key. Voting belongs to the vote node: method, quorum, threshold, allow_abstain, and timeout_seconds. Debate rounds use config.max_rounds.
Start and observe a run
Only a published version can run. Link the source Flownix nodes so the run remains discoverable.
harness.start_run({ workspace_id, harness_id, harness_version_id, title: "Review AF-FEAT-10", input: { question: "Is this design ready?" }, source_node_ids: [feature_node_id] })
harness.get_run({ run_id })
harness.list_runs({ workspace_id, source_node: "AF-FEAT-10" })harness.get_run returns steps, outputs, work items, usage, and remaining budget. A run exceeding duration, work-item, reported-token, or reported-cost limits fails with budget.exceeded.
External agent participation
work.list_available({ run_id, role_key: "reviewer" })
work.claim({ work_item_id, agent_id: "reviewer-1" })
work.get_context({ work_item_id })
work.heartbeat({ work_item_id })
work.complete({ work_item_id, outcome: "approved", result_artifact_ids: ["AF-DOC-12"], output: { summary: "Ready" }, usage: { input_tokens: 12000, output_tokens: 1800, cost_usd: 0.15 } })For a vote work item, take ballot_id and option IDs from work.get_context, then call council.cast_vote or council.abstain; the vote closes the work item, so do not call work.complete. A judge reads the shared council with council.get_deliberation and closes its step with council.submit_judgement.
Human approval and creating Flownix nodes
harness.list_pending_approvals({ run_id }) explains why a run is in waiting_for_user; only a person can answer in the web app. Put a human_gate on every path to create_flow_nodes. The preceding agent returns output.flow_nodes, and the runtime creates them through the normal Flownix checks. Repeating the step is idempotent by (run, key).
Only tools policies are enforced, and only for work.* / council.* calls. Other policy categories are stored and return policy.not_enforced; they do not constrain an external agent's shell, files, or other services.