Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Putting a person in the loop

Some steps belong to a human: an approval, or a correction the model should not make alone.

Getting a program to wait for one is normally the expensive part, because something has to remember where the flow was and wake it up again without losing anything, which usually means a queue, a webhook and a state machine.

In weft it is a node.

review = HumanQuery {
  title: "Escalate this ticket?"
  fields: [{ "kind": "approve_reject", "key": "escalate" }]
}

Wire something into it, wire its outputs onward, and run. The execution reaches review, suspends, and the worker process exits rather than blocking.

The task appears wherever a person can answer it. When they do, a fresh worker starts, rebuilds the execution’s state from the journal, and continues from exactly the point it stopped. That gap can be four seconds or four weeks; the code is identical and so is the cost, which is one row in a table.

The ports come from the form

HumanQuery has no fixed output ports. Its ports are derived from the fields you configured, at compile time.

The approve_reject field keyed escalate produces two Boolean outputs:

  • review.escalate_approved
  • review.escalate_rejected

You never declare those. Add a text_input field keyed reason and you get a review.reason String output alongside them. Change the form and the ports change with it, and every wire you had is re-checked against the new shape.

So this compiles or it does not:

alert = SlackSendMessage {
  _should_flow: review.escalate_approved
  channel: "#oncall"
  text: classify.response
}

_should_flow is on every node and decides whether it runs. A false there skips the node, which closes its outputs, which skips everything behind it, so rejecting the escalation ends that branch on the spot. That is how branching works here, and it is covered properly in How a weft program runs.

Where the task shows up

Tasks reach people through the weft browser extension. Build it with:

./setup.sh --browser --no-sign

--no-sign skips signing the add-on with Mozilla, which you do not need for a local install and which fails without AMO API keys.

Load the unpacked build, then connect it to your runtime with a token:

weft token mint --name "my laptop"

That prints a connect URL (and the bare token on a second line) exactly once, because the server stores only a hash of it. Paste it into the extension and pending tasks start arriving.

A token can also be narrowed to one project or one kind of task, which is how you hand a reviewer something that only ever shows them their own queue. That, and the per-browser loading steps, are in The browser extension.

Watch the handoff

Run the program with the graph open. The HumanQuery node goes into its waiting state and stays there. Answer in the extension and the graph continues in front of you.

Answer it tomorrow instead and you get the same result, because the execution is rows in a table rather than a process holding state. The journal covers what those rows contain.

Next: when something goes wrong.