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:

SituationDefault routing
No unblocked workWait — no decision required
One unblocked task with an unambiguous outcomeAuto-close via declared rule
Multiple outcomes possible for a task or goalRoute to the declared decider (task > goal > process > workflow > engine default)
Node declares a specific deciderRoute 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:

  1. Read instance state from the workflow service
  2. Enumerate legal moves — close a task, close a goal, add a task, deprecate a task, or wait
  3. Decide — route to the appropriate decider (rule or LLM) based on the workflow's declaration
  4. Apply the decision through the workflow service's control plane
  5. Persist a reasoning trace on the workflow node
  6. 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:

FormMeaning
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
engineBuilt-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