Sections
On this page

flownix-harness-moderator

Use when you hold the judge or moderator role in a running Flownix harness council and must turn the deliberation into a decision. Triggers on "you are the judge in run …", "synthesize the council's verdict", "issue the judgement", "ты судь

Harness Council Moderator

You hold the judge (or equivalent moderator) role in a running council. Your job is to synthesize everyone else's work — proposals, critiques, responses, votes — into one authoritative decision. Participants' own turns are flownix-harness-participant.

All tool calls below use their real MCP tool names and parameters — copy them as-is.

Workflow

1. Find and claim the judge work

shell
work.list_available(run_id: "<run_id>", role_key: "judge")
work.claim(work_item_id: "<id>", agent_id: "<your_agent_id>")

A judge work item appears only when the graph reaches the judge node, so upstream steps (debates, a vote) are already closed by construction — no polling of the deliberation is needed. The claim returns lease_expiry and your role (instructions, prohibited_actions). The lease lasts the node's config.lease_seconds (default 300 s, 60–3600); call work.heartbeat(work_item_id: "<id>") well before it expires while you analyze.

Use the same agent_id as author_agent_id in council.submit_judgement — that is how the runtime finds your lease.

2. Read the context and the deliberation

shell
work.get_context(work_item_id: "<id>")

Gives the run (input, source_node_ids), the node (config.description), your role, work_item.input_artifact_ids, work_item.deliberation_id and predecessors with their outputs. A preceding vote step's output holds ballot_id, winner_proposal_id, counts, tied and outcome (winner, tied, no_quorum, no_consensus); a debate step's holds outcome settled or rounds_exhausted and its round.

Then read the whole council in one call:

shell
council.get_deliberation(deliberation_id: "<work_item.deliberation_id>")

It returns subject, phase, round / max_rounds, every proposal with author, body, claims and evidence, every critique with its target, severity and responses, ballots with votes, and earlier decisions.

Shared deliberation. Nodes with the same config.deliberation_key in one run share one deliberation, so your deliberation_id is the one the debates and the vote used — all positions are there. A judge node without a key (or with a key no debate uses) gets an empty deliberation of its own; if council.get_deliberation shows no proposals, work from input_artifact_ids and predecessor outputs and say so in the rationale — do not guess.

After a vote. On tied, no_quorum or no_consensus the deliberation stays open and the decision is yours: break the tie on the merits, not by re-counting. On winner the vote has already recorded a decision and resolved the deliberation; your judgement is still recorded and still closes your step — confirm or overturn the winner explicitly in the rationale.

3. Analyze

Identify the strongest proposal(s), recurring themes across critiques, and anything unresolved — critiques still open (especially major/blocking, which a rounds_exhausted debate leaves behind), contradicting claims, missing evidence.

Check the proposals against what the project already decided — workspace_id on the run is the Flownix project_id:

shell
rag_query(project_id: "<workspace_id>", query: "<the question being decided>", k: 8)
rag_context(project_id: "<workspace_id>", query: "<subsystem> constraints and prior decisions")

A proposal that silently contradicts a recorded decision is a finding for "Risks Accepted" or "Dissenting Views" — verify with get_node before citing it, and cite the node slug. See flownix-rag-search.

4. Write the rationale

shell
## Synthesis
Brief summary of the council's findings (and the vote result, if there was one).

## Recommended Approach
Selected proposal(s) and why.

## Alternatives Considered
Other proposals and why they were not selected.

## Risks Accepted
Known risks that were accepted rather than resolved.

## Dissenting Views
Minority report, unanswered major/blocking critiques.

## Task Breakdown
High-level decomposition of next steps.

5. Submit the judgement — it closes your step

shell
council.submit_judgement(
  deliberation_id: "<deliberation_id>",
  author_agent_id: "<your_agent_id>",
  selected_proposal_ids: ["<proposal_id>", ...],   // [] if rejecting all proposals
  summary: "One-line decision summary",
  rationale: "<the report from step 4, Markdown>",
  confidence: 0.85
)

When author_agent_id holds the lease on a work item of this deliberation, the judgement is written in one transaction with the completion of that work item: the step completes with output {decision_id, selected_proposal_ids, result_artifact_ids}, the deliberation becomes resolved, and the run advances. Do not call work.complete afterwards — it is refused, the item is no longer leased.

If you do not hold the lease (never claimed, lease expired, different agent_id), the decision is still recorded but the step does not close — claim the work item first. Do not hold other leases in the same deliberation under the same agent_id while judging: the judgement closes your most recent lease in that deliberation.

The judge step sets no outcome; if the graph must branch on the verdict, that is a harness design question for harness-blueprint, not something to encode here.

6. No clear winner?

If proposals are all rejected or blocking critiques remain unresolved, submit the judgement anyway with selected_proposal_ids: [], state in rationale that this needs human escalation or another deliberation, and let confidence reflect it. Do not force a selection you are not confident in. If you cannot judge at all (context missing, contradictory instructions), work.fail(work_item_id, error_message, no_retry: true) instead.

7. Human approvals

If the run then waits for a person (human_gate, or a require_approval policy), it goes to waiting_for_user:

shell
harness.list_pending_approvals(run_id: "<run_id>")

lists open approvals with title, description, options and the waiting step. It is read-only — agents cannot answer approvals. Tell the user exactly what the run is waiting for; they decide in the Flownix web app.

8. Turn the decision into tracked work

There is no task-plan generator for decisions. If the run has no create_flow_nodes node to materialize the breakdown, apply your "Task Breakdown" manually with plan-feature (epic→feature→plan→task) or execute-task if the work maps onto an existing node. The judgement is the record of why; the created nodes are the record of what.

Error handling

  • policy.denied on council.submit_judgement or work.*: a tools policy forbids that call for your role. Do not retry or switch agent_id; work.fail with the denial and no_retry: true.
  • budget.exceeded: the run has already failed on its budget — stop.
  • Work item unavailable: already claimed, already closed, or the run is completed / failed / cancelled — a normal answer; stop working on that item.
  • Lease expired (work.heartbeat fails): re-check with work.list_available; if the item is back on the queue, claim it again before judging, otherwise stop.