# How LoopX's Turn Decision Vocabulary Works: Turn Route and Turn Result Kind Explained

> Understand LoopX's turn decision vocabulary. Learn how LoopXTurnRoute directs provider steps and LoopXTurnResultKind classifies outcomes for seamless workflow transitions.

- Repository: [huangruiteng/loopx](https://github.com/huangruiteng/loopx)
- Tags: deep-dive
- Published: 2026-09-04

---

**LoopX models every turn as a transactional workflow where the Turn Route determines which provider step executes next and the Turn Result Kind classifies the semantic outcome, with both concepts tightly coupled in the settlement engine to ensure valid state transitions.**

In the LoopX control plane, turn settlement is the core mechanism that orchestrates provider-side effects and communicates results back to callers. The system uses a precise decision vocabulary built around two TypeScript constructs: `LoopXTurnRoute` (the ordered pipeline of provider steps) and `LoopXTurnResultKind` (the public enumeration of turn outcomes). This article examines how these abstractions interact in [`loopx/control_plane/turn_driver/settlement.ts`](https://github.com/huangruiteng/loopx/blob/main/loopx/control_plane/turn_driver/settlement.ts) to drive deterministic execution and outcome validation.

---

## What Is the Turn Route in LoopX

The **Turn Route** is the ordered sequence of provider steps that every turn must traverse. In [`settlement.ts`](https://github.com/huangruiteng/loopx/blob/main/settlement.ts), this route is defined by the constant `BASE_SETTLEMENT_STEPS` at lines 33-38:

- `validation` — validates the turn request
- `durable_writeback` — persists the turn state
- `quota_spend` — consumes quota for the operation

When a terminal close-out is required, the route extends with an additional `"terminal_closeout"` step.

The settlement engine computes the route dynamically at runtime using `settlementNextAction`. This function inspects `completed_phases` against `BASE_SETTLEMENT_STEPS` to determine the next provider effect to prepare or execute. The route is therefore deterministic: given the same completion state, the engine always selects the same next step.

```typescript
import { settlementNextAction } from "./loopx/control_plane/turn_driver/settlement.ts";

const next = settlementNextAction({
  identity,
  ordered_steps: ["validation", "durable_writeback", "quota_spend"],
  committed_payloads: { validation: {}, durable_writeback: null, quota_spend: null },
  completed_phases: [],
  transaction_phases: ["validation", "durable_writeback", "quota_spent"],
  require_validation: true,
  source_ref_prefix: "turn_journal",
});

console.log(next.step_kind);  // → "validation"

```

---

## What Is the Turn Result Kind

The **Turn Result Kind** is a public enumeration of semantic outcomes that a turn can report. Defined at lines 49-63 in [`settlement.ts`](https://github.com/huangruiteng/loopx/blob/main/settlement.ts), the constant `TURN_RESULT_KINDS` exposes valid values typed as `TurnResultKind`. These include:

- `"validated_progress"` — turn made forward progress
- `"validated_completion"` — turn finished successfully
- `"validation_failed"` — validation rejected the request
- `"host_failure"` — unrecoverable error occurred

The result kind is attached to every settlement request via the `turn_result_kind` field and later projected into the final outcome through `reductionWithTurnOutcome`.

Certain kinds are classified as failures in `FAILED_TURN_RESULT_KINDS`. The system rejects any attempt to complete a turn with these kinds, preventing invalid state transitions.

```typescript
import { reduceTurnSettlementTransaction } from "./loopx/control_plane/turn_driver/settlement.ts";

const request = {
  schema_version: "loopx_turn_settlement_transaction_v0",
  transaction_plan: {/* … */},
  transaction_phases: ["validation", "durable_writeback", "quota_spend"],
  completed_phases: [],
  committed_effect_id: null,
  writeback_payload: null,
  quota_spend_payload: null,
  terminal_closeout_required: false,
  terminal_closeout_payload: null,
  failed_provider_attempt: null,
  effect_attempts: {},
  provider_observations: {},
  turn_result_kind: "validated_progress",  // ← selected from TURN_RESULT_KINDS
};

const reduction = reduceTurnSettlementTransaction(request);

```

---

## How Turn Route and Turn Result Kind Interact

The settlement engine tightly couples routing and result classification through four mechanisms:

### 1. Route-Driven Step Selection

`settlementNextAction` uses `BASE_SETTLEMENT_STEPS` to advance the turn. The function examines which phases are complete and returns the next unexecuted step in `base.step_kind`. This ensures the Turn Route progresses deterministically regardless of the result kind.

### 2. Result-Kind Validation

Before finalizing, `validateTurnOutcomeKind` checks the requested `turn_result_kind`. If it belongs to `FAILED_TURN_RESULT_KINDS`, the settlement aborts:

```typescript
const badRequest = { /* …request… */, turn_result_kind: "validation_failed" };
try {
  reduceTurnSettlementTransaction(badRequest);
} catch (e) {
  console.error(e.message);
  // "Turn settlement cannot complete with failed result_kind validation_failed"
}

```

### 3. Result-Kind-Conditional Routing

Specific result kinds influence route behavior:

- **Non-terminal completion**: When `turn_result_kind === "validated_completion"` and no terminal close-out is required, the engine validates that the write-back payload does not contain an illegal `"no_followup"` continuation (`nonTerminalCompletionError` check).

- **Terminal close-out requirement**: If `terminal_closeout_required === true`, the route extends with `"terminal_closeout"` and the result kind must be `"validated_completion"`; otherwise `terminalCloseoutRequestFailure` rejects the request.

### 4. Outcome Projection

When settlement finishes, `reductionWithTurnOutcome` encodes the `turn_result_kind` into the turn outcome payload under the key `result_kind`. The Python adapter and downstream consumers read this field to interpret the turn's semantic result.

---

## Source Code Deep Dive

Understanding these interactions requires examining three files:

| File | Relevance |
|------|-----------|
| [`loopx/control_plane/turn_driver/settlement.ts`](https://github.com/huangruiteng/loopx/blob/main/loopx/control_plane/turn_driver/settlement.ts) | Defines `BASE_SETTLEMENT_STEPS`, `TURN_RESULT_KINDS`, validation logic, and the core reduction functions that bind route and result kind together |
| [`tests/control_plane_ts/turn_settlement.test.ts`](https://github.com/huangruiteng/loopx/blob/main/tests/control_plane_ts/turn_settlement.test.ts) | Unit tests verifying correct routing decisions, result-kind validation, and error handling paths |
| [`tests/test_loopx_turn_transaction.py`](https://github.com/huangruiteng/loopx/blob/main/tests/test_loopx_turn_transaction.py) | Python adapter demonstrating cross-language consumption of `turn_result_kind`, confirming the vocabulary contract |

The settlement module exports the key functions that implement the decision vocabulary:

- `reduceTurnSettlementTransaction` — main entry point for turn processing
- `settlementNextAction` — computes the next step from the current route position
- `validateTurnOutcomeKind` — enforces valid result kinds before completion

---

## Summary

- **Turn Route** (`BASE_SETTLEMENT_STEPS`) is the deterministic provider-step pipeline: validation → durable_writeback → quota_spend, optionally extended with terminal_closeout.

- **Turn Result Kind** (`TURN_RESULT_KINDS`) classifies semantic outcomes, with `FAILED_TURN_RESULT_KINDS` preventing completion on failure states.

- The settlement engine couples both: route selection drives execution order while result-kind validation ensures only valid outcomes are committed.

- Cross-language contracts are maintained through the `result_kind` field in the final outcome payload, consumed by Python adapters and downstream systems.

---

## Frequently Asked Questions

### What happens if a turn tries to complete with a failed result kind?

The settlement engine rejects the request. In `validateTurnOutcomeKind`, if the `turn_result_kind` belongs to `FAILED_TURN_RESULT_KINDS` (such as `"host_failure"` or `"validation_failed"`), the function throws with the message "Turn settlement cannot complete with failed result_kind [kind]". This prevents corrupted terminal states.

### Can the Turn Route change during settlement?

The base route defined by `BASE_SETTLEMENT_STEPS` is fixed, but the engine conditionally appends `"terminal_closeout"` when `terminal_closeout_required` is true. The route is otherwise deterministic—`settlementNextAction` always selects the first uncompleted step in the ordered sequence.

### How does the Python side consume the Turn Result Kind?

The Python adapter reads the `result_kind` field from the outcome payload produced by `reductionWithTurnOutcome`. This field carries the original `turn_result_kind` value across the language boundary, allowing Python code to branch on turn outcomes using the same semantic vocabulary defined in TypeScript.

### What is the difference between "validated_progress" and "validated_completion"?

`"validated_progress"` indicates the turn advanced through its route but remains active for future continuation. `"validated_completion"` signals terminal success—when used without terminal close-out, the engine validates that no illegal `"no_followup` continuation exists; when terminal close-out is required, it enables the additional close-out step in the route.