Decision Engine
The decision engine turns workflow state into workflow behavior. It sits between the transport layer (where messages arrive) and the workflow service (which mutates state and enforces constraints). The workflow service is a pure state machine with no opinion about what should happen next — the decision engine provides that opinion.
Two modes
The decision engine operates in two modes, declared per decision point in the workflow definition:
Deterministic mode
Evaluates rules declared in the workflow definition. For decisions that are pure functions of engine state:
- Auto-advance when all required goals are satisfied
- Auto-escalate when a timer fires past an SLA
- Auto-pause when evidence is invalidated
- Cascade-close on parent terminate
Deterministic rules are synchronous, pure, and auditable. Most decisions in a well-structured workflow are deterministic.
Agentic mode
Uses your Skills bundles to drive LLM-based decisions. For situations where multiple legal next steps exist and judgment is required to choose among them.
The agentic decider loads the Skills bundle — role context, escalation policy, evidence evaluation criteria, example decisions — and runs it against an LLM with the current workflow state as context. The decision is persisted with a reasoning trace.
When does each mode fire?
The routing is based on ambiguity:
| Situation | Default routing |
|---|---|
| No unblocked work | Wait — no decision required |
| One unblocked task with an unambiguous outcome | Auto-close via declared rule |
| Multiple outcomes possible for a task or goal | Route to the declared decider (task > goal > process > workflow > engine default) |
| Node declares a specific decider | Route accordingly, regardless of ambiguity |
This means most decisions never reach an LLM. Only the genuinely ambiguous ones do.
The controller loop
When a signal arrives or a workflow event fires, the decision engine enters a loop:
- Read instance state from the workflow service
- Enumerate legal moves — close a task, close a goal, add a task, deprecate a task, or wait
- Decide — route to the appropriate decider (rule or LLM) based on the workflow's declaration
- Apply the decision through the workflow service's control plane
- Persist a reasoning trace on the workflow node
- Repeat until the decision is to wait
The decision engine has no privileged write path. Its decisions go through the same control plane as a human's — same audit row, same evidence enforcement, same authorization checks.
Skills bundles
A Skills bundle is content, not code. It describes your goals, best practices, judgment criteria, and escalation policy for a workflow domain — not the steps to take in order.
Good Skills content reads like guidance to a thoughtful new colleague:
- "Strong candidates usually have at least three solid references"
- "If the condition assessment shows major structural damage, escalate to the landlord before scoping repairs"
- "For routine cleaning delays under 24 hours, reschedule rather than reassign"
Bad Skills content reads like a runbook:
- "Step 1: schedule the screen interview. Step 2: if successful, request references."
The distinction matters: because Skills describe goals and judgment rather than procedure, the procedural reasoning lives in the LLM. As models improve, your declared intent benefits automatically — your Skills keep working as the models behind them get smarter, without any rewriting on your part.
Bundle structure
A Skills bundle is organized as a directory of markdown files with a manifest:
- manifest.json — version, decider names served, model preferences
- Role context — what the decider cares about, how it thinks
- Escalation policy — when to defer to humans
- Evidence evaluation — how to read each evidence type
- Example decisions — few-shot examples of good decisions in context
Decider grammar
The workflow definition declares a decider clause that may attach at any level — workflow, process, goal, or task — with the most specific declaration winning:
name: hiring.standard
decider: agent:hiring_judgment # workflow-level default
processes:
- name: screen
decider: rule:auto # process-level override — deterministic
goals:
- name: resume-reviewed
autoClose: true
tasks:
- { name: review-resume, decider: agent:hiring_judgment }
- name: interview
goals:
- name: panel-completed
decider: human:hiring_manager # goal-level override — human only
tasks:
- { name: schedule-panel }
- { name: conduct-panel, depends_on: [schedule-panel] }
Resolution precedence: task > goal > process > workflow > engine default. The decision engine resolves the chain at decision time, not at instantiation, so changes to the specification's defaults apply to in-flight instances on the next decision.
Decider values:
| Form | Meaning |
|---|---|
rule:<name> | Named declarative rule, evaluated synchronously |
agent:<name> | Named agentic decider, loads Skills bundle, invokes LLM |
human:<role> | Escalate to a human in the named role |
engine | Built-in engine behavior (cascading rules) |
A single workflow can mix all forms. Deterministic for routine closures, agentic for judgment-heavy ones, human escalation for the genuinely irreducible.
Reasoning traces
Every decision the engine makes is auditable. The reasoning trace is persisted on the affected workflow node and captures:
- Which decider was invoked and the node's
publicId - What candidates were considered
- What was selected and why
- Any follow-up writes triggered by the decision (e.g., deprecating a planned task, adding a response task)
- Confidence level (for agentic decisions)
- Citations to evidence and Skills content
- Latency
{
"decider": "agent:hiring_judgment",
"node_public_id": "wf-abc123.screen.7c1f9e.1",
"selected": { "action": "close", "reason": "failed" },
"follow_up_writes": [
{ "action": "deprecate_task", "node_public_id": "wf-abc123.interview.9a2b1c.1" },
{ "action": "add_task", "under_goal": "wf-abc123.screen.7c1f9e", "name": "reject-candidate" }
],
"confidence": 0.78,
"reasoning_excerpt": "Resume shows two years total experience; role requires five+. Closing review-resume failed and inserting reject-candidate."
}
The trace is persisted alongside the goal or task mutation it produced, readable through the same decision log that captures human decisions.
How it works
The decision engine operates as an event-driven loop:
- Per-event loop — the engine subscribes to workflow change events, loads state, invokes the appropriate decider, and writes back through the workflow service control plane.
- Agentic decider — backed by an LLM with tool-use access to the workflow service write methods (
updateTaskStatus,updateGoalStatus,addTask,deprecateTask,autoCloseGoal).
Example: In a hiring workflow, a review-resume task receives a poor-fit result. The engine closes the task as failed, deprecates schedule-interview, and adds a reject-candidate task — all in a single decision cycle with a persisted reasoning trace.
What's next
- Skills Authoring — how to write effective Skills bundles
- Composable Workflows — the state machine grammar
- Operational Metrics — metrics derived from decision and workflow data