A vendor review with a reason for every branch
Run a small team that evaluates a fictional software vendor from a supplied source pack. It routes a narrow question to one fact checker, sends a broader review to two researchers in parallel, classifies their evidence, and brings in a senior reviewer only when sources disagree. A writer produces a cited brief. Its submission must pass a bounded grounding review before the workflow returns it.
npm ci
npm run build
node examples/vendor-review.mjs conflict
No key is required. The AgentRun interpreter executes the graph, branches, parallel work, state mappings, escalation, output schemas, and submission verification. The agent and judge responses are deterministic scripts. They demonstrate the host interfaces; they do not demonstrate model quality, real vendor research, or measured confidence. All source documents and vendor claims are fictional.
Try a different input
| Command | What changes |
|---|---|
node examples/vendor-review.mjs pilot |
Two researchers find consistent evidence. The writer's first draft passes. |
node examples/vendor-review.mjs conflict |
Two deletion policies disagree. A senior reviewer finds an explicit superseding amendment. The first writer draft lacks a valid citation; the second passes. |
node examples/vendor-review.mjs quick |
One integration question uses a fact checker and skips the research team. |
node examples/vendor-review.mjs missing |
The source pack has no deletion policy. The workflow stops with a human handoff and no recommendation. |
Add --json to any command for the input, result, actual interpreter events, and adapter calls. The conflict trace contains two verify.answered events: accepted: false, then accepted: true. These events come from the interpreter's real verify clause.
The workflow
Scope router
/ \
quick / \ full
v v
Fact checker Product researcher ─┐
Security researcher ─┴─ parallel join
\ /
Evidence classifier
supported / conflicting / missing
|
Review gate
zero or one review item
|
Senior reviewer
only for a conflict
|
Evidence gate
/ \
unresolved or missing sufficient
v v
Human handoff Brief writer
↖ ↘
repair ← verify
max 2 reviews
|
Final brief
The router makes a typed choice. The classifier answers a separate typed question: are all required topics supported, conflicting, or missing? Neither produces an opaque instruction for the engine to interpret.
A code node turns conflicting into a one-item review queue. A map runs the senior reviewer on that item; an empty queue means no reviewer session. The reviewer must find explicit documentary authority to resolve a contradiction. It cannot resolve missing evidence by writing a plausible answer. The next code node sends missing or unresolved evidence to an escalate node.
The writer's verify clause applies two requirements: claims must be grounded in the accepted evidence, and the conclusion must stay within the requested scope. The conflict fixture deliberately removes a citation from the first draft. The scripted writer calls the supplied review hook, receives the rejection, repairs that draft, and resubmits. The interpreter enforces a maximum of two reviews and also checks returned submissions if an adapter ignores the hook. Exhaustion throws WorkflowVerificationError; it does not return an accepted brief.
The final code node records delivery in workflow state. There is no email, external publication, procurement action, or production approval. A full-review brief recommends only a proposed 14-day pilot with synthetic data, subject to human approval.
Read the result
A successful conflict run returns five cited findings: integrations, EU residency, the proposed pilot scope, deletion, and access controls. Every finding retains its source ID and exact quotation. The original 90-day deletion finding remains in research evidence; the accepted brief uses the 30-day statement from the amendment that explicitly supersedes it.
The missing-evidence run instead returns:
{
"status": "escalated",
"escalation": {
"label": "human-handoff",
"kind": "missing_or_conflicting_evidence",
"stage": "evidence-review",
"summary": "Request a documented deletion policy from the vendor before recommending a pilot."
}
}
This excerpt omits the retained workflow state. The JSON trace contains that state, including source documents and completed findings, so a person can see why the run stopped.
Use your own agents and judge
The example exports its graph and runner from examples/vendor-review.mjs. Keep the workflow; replace the two host seams:
import { runVendorReview } from './examples/vendor-review.mjs';
const run = await runVendorReview('conflict', {
runNode: yourAgentAdapter,
runJudge: yourJudgeAdapter,
onEvent: event => console.log(event.type, event.label),
});
runNode receives the agent role, an explicit JSON state projection, an output schema, and the optional submission-review hook. For the writer, connect that hook to the agent's submission tool so a rejected draft can be corrected within the same session. runJudge receives typed questions and projected evidence and returns answers in the documented System One format. Jev can supply that judge seam; the example does not choose a provider or model.
Host mode requires both adapters, labels its output mode: "host", and retains the same graph and execution limits. The host owns inference, tool permissions, network access, credentials, and session budgets. A runNode implementation that returns a rejected draft without using the hook fails the run; the interpreter does not invent a replacement session.
For your own source pack, import workflow and call the public runWorkflow(workflow, input, deps) API. Replace the fictional documents and adapt the research instructions and output schemas to your task. The scripted source pack includes facts annotations to make this no-key example deterministic; a real research adapter would extract evidence from documents or tools. The scripted verifier accepts only the reviewed summary, title, limitations, and next steps for these fixed source packs, plus the complete accepted claim set with valid quotations. This deliberately rejects even harmless paraphrases: it is an exact fixture check, not semantic evaluation of arbitrary prose. Treat the scripts as fixtures, not a production evidence classifier or verifier.
What is tested
node --test examples/vendor-review.test.mjs
The tests exercise all four inputs, prove both researchers overlap, check citation provenance, verify the senior reviewer is conditional, and witness the failed and repaired submissions. They also force unresolved review, an adapter that ignores the review hook, and two rejected submissions. Each case checks that an unaccepted result never reaches finalization.
Source: examples/vendor-review.mjs. Tests: examples/vendor-review.test.mjs. Primitive contracts: DSL guide.