# Decision Branch Node

> Applies to: ChengOS v0.2.0+ ｜ Last verified: 2026-09-25 ｜ Source: `crates/cheng-nodes/src/nodes/builtin/ai/decision_branch.rs`, `config/decision_providers.toml`, `config/decision_calibration.toml`

`ai/decision_branch` routes a conversation turn to exactly one of the routes you declare, using a
typed decision provider rather than a chat model: TypeSafe Jev (hosted, calibrated
probabilities), Laya (a local model the operator runs), or a connected LLM as an uncalibrated
baseline. The provider sees only the latest request, the route descriptions, the routing
instruction, and in sticky mode the previous route ID — never history, tools, or credentials.
The context itself is delivered unchanged to `route_<id>`; anything ordinary, ambiguous,
multi-intent, low-confidence, or failed goes to `fallback_out`, so a misroute or an outage
degrades to what the workflow did without the router. Authoring guidance, thresholds, and the
two-stage cascade are in [Semantic Routing](/docs/agent-semantic-routing); provider setup is in
[Decision Providers & TypeSafe Jev](/docs/decision-providers-and-jev).

## Decision Branch — `ai/decision_branch`

Route the request to exactly one declared route with a typed decision model (Jev or an LLM baseline); anything unclear goes to fallback_out with the context unchanged

### Inputs

| Field | Type | Required | Default | Description |
|---|---|:-:|---|---|
| `context` | object (port) | — | `null` | Input AgentContext; the latest user request is routed and the context is delivered unchanged. Connection port, not a form field. Control: `code-editor` |
| `provider` | enum | — | `jev` | Jev: hosted typed decision model (the routed request is sent to TypeSafe). LLM: a connected chat model as an uncalibrated baseline. Laya: the installation's own local model, when the operator has enabled it (at most 15 routes; uncalibrated unless the operator pins the served artifact). Control: `select`. Options: `jev` (TypeSafe Jev (hosted; calibrated probabilities).), `llm` (A connected chat model with constrained output (uncalibrated).), `laya` (The installation's own Laya deployment (local; enabled by the operator).) |
| `credential_id` | string | — | `null` | TypeSafe Jev credential. Leave empty to use the deployment's JEV_API_KEY, if configured. Control: `credential-select` |
| `decision_model` | string | — | `""` | Decision model; empty uses the credential's pin, then the provider default. Wins over the legacy `jev_model` when set. Control: `decision-model-select` |
| `llm_model` | string | — | `null` | Chat model for the LLM baseline provider. Control: `llm-model-select` |
| `routing_instruction` | string | — | `""` | How to interpret the request and when to choose each route. Route IDs come from the route list; keep tool-operation instructions on the focused LLMs. Control: `textarea` |
| `routes` | array&lt;object&gt; | — | `[]` | Declared routes (keyed output ports). Control: `route-list` |
| `route_memory` | enum | — | `sticky` | Sticky: send the previous route as context; in calibrated mode it is also held for a referential follow-up (in none mode there are no hold thresholds, so only the context applies). Remembered per conversation in this server process. Off: route every turn on its own. Control: `select`. Options: `off` (Every turn is routed on its own.), `sticky` (The previous turn's route is sent as context, and may be held for a referential follow-up ("reply to him and say yes").) |
| `threshold_mode` | enum | — | `calibrated` | Calibrated: apply the measured profile for this provider/model/language, or fall back when none exists; providers that report no probabilities (LLM baseline) cannot use it. None (uncalibrated): route on the raw answer. Control: `select`. Options: `calibrated` (Apply the measured calibration profile; without one, fall back.), `none` (Uncalibrated: route on the raw answer; only invalid, out-of-set, timed-out, or failed decisions fall back.) |
| `failure_mode` | enum | — | `fallback` | Fallback: provider errors and missing configuration go to fallback_out. Fail closed: they fail the node. Control: `select`. Options: `fallback` (Failures and missing configuration go to `fallback_out`.), `fail_closed` (Failures and missing configuration fail the node with a typed error.) |
| `uncertain_routing` | enum | — | `fallback` | Fallback: low-confidence decisions go to fallback_out with everything else. Separate port: in calibrated mode they go to uncertain_out (below the threshold, or no profile for this model/language), so a second-stage arbiter can decide them; confident fallbacks and provider failures still go to fallback_out. Control: `select`. Options: `fallback` (Together with every other non-route outcome, to `fallback_out`.), `separate` (To `uncertain_out`, so a second-stage arbiter can decide it while confident fallbacks (ordinary conversation) go straight to `fallback_out`.) |
| `timeout_ms` | integer | — | `null` | Decision deadline; empty uses the provider's default (Jev and Laya: 1500 ms). For the LLM baseline an empty timeout adapts to the chosen chat model from its observed decisions, so a reasoning model gets the time it needs. A miss follows the failure mode and never stalls the turn. Values above the provider's maximum are capped. |

### Outputs

| Field | Type | Description |
|---|---|---|
| `fallback_out` | object (port) | Unchanged context when no route was selected. Connection port, not a form field. |
| `uncertain_out` | object (port) | Unchanged context for a low-confidence decision, when separated. Connection port, not a form field. |
| `decision` | object (port) | Bounded decision summary (no user text). Connection port, not a form field. |

## Next steps

- [Semantic Routing with Decision Branch](/docs/agent-semantic-routing)
- [Decision Providers & TypeSafe Jev](/docs/decision-providers-and-jev)
- [LLM Nodes](/docs/nodes-llm)
