---
url: 'https://adk.nht.io/batteries/validation/modes.md'
description: >-
  Configure enforce vs mutate operating modes, understand automated timeline
  repair strategies, and wire ordering guard middleware into ADK turn loops.
---

# Operating Modes & Repair Strategies

## LLM summary — Operating Modes & Repair Strategies

* Two operating modes configured via `action`:
  * `'enforce'` (default): Strict validation. Any blocking violation halts execution via `ctx.nack(error)` in dispatch middleware, `ctx.abort(error)` in turn middleware, or throws `E_ORDERING_VIOLATION` if `onViolation: 'throw'`. It does not mutate context Sets. Recommended for development/CI, where a loud failure is the point.
  * `'mutate'`: Best-effort automated repair. It repairs ordering violations, inserts alternation fillers, and optionally fills missing vendor metadata through double opt-in. The guard then re-evaluates the repaired timeline; only genuinely unrepairable violations trigger `onViolation`. Recommended for production, where continuity of a live turn matters more than rejecting a mechanically-fixable quirk.
* **Repair capabilities & distinctions:**
  * `OrderRule`: Nudges the offending primitive's `createdAt` (via `ctx.mutateToolCall`/`mutateThought`/`mutateMessage`) so it sorts immediately before the primitive it must precede, then re-evaluates the repaired timeline to confirm the fix holds. This reaches the real turn state directly — an LLM adapter's own history assembly sees the corrected order on its next pass, with no separate stash hint to consume.
  * `AlternationRule`: Materializes synthetic role filler messages directly into history via `ctx.storeMessage`.
  * `RequiredMetadataRule`: Repaired only when BOTH `action: 'mutate'` AND `allowMetadataFallbackRepair: true` are enabled (double opt-in).
  * `PreservationRule` / `AdjacencyRule`: Never repairable (dropped history cannot be fabricated; interleaving cannot be dropped safely).
* **Options interface:** [`OrderingGuardOptions`](https://adk.nht.io/api/@nhtio/adk/batteries/validation/types/interfaces/OrderingGuardOptions) configures `profiles`, `mode`, `action`, `onViolation`, `onRepair`, `allowMetadataFallbackRepair`, `snapshotStashKey`, and `disableAdvisoryRuleIds`.

In production agent loops, keeping a conversation moving is often as critical as validating protocol correctness. Most validation libraries treat rejection as the only civilized response to a rule violation, even when what failed was a trivially fixable ordering quirk. Refusing to fix something you can deterministically repair isn't rigor; it's laziness that costs users a live turn.

The ordering guard battery supports two operating modes configured by the `action` option in [`OrderingGuardOptions`](https://adk.nht.io/api/@nhtio/adk/batteries/validation/types/interfaces/OrderingGuardOptions): one that trusts nothing and refuses to touch your state, and one that actually tries to keep your execution loop alive.

::: tip TL;DR — which mode do I actually want?
**`enforce` in development and CI. `mutate` in production.**

* **`enforce`** is your smoke detector. It will not put out the fire for you, and it will not pretend the fire isn't there. Use it while you're writing adapters, debugging a pipeline stage, or running conformance tests against synthetic traces — anywhere you *want* a violation to be loud, because a loud failure right now is what stops a silent one from reaching a real user later.
* **`mutate`** is what you actually ship. A real user mid-conversation does not care that a vendor's chat template stamped a `Thought` a millisecond after its `ToolCall` — they care that their agent kept working. `mutate` repairs what's safely repairable and only escalates the violations that genuinely can't be fixed without inventing facts, so production traffic gets continuity instead of a 400 for a bug that was never theirs to cause.

Shipping `enforce` to production is how a mechanically-fixable ordering quirk becomes a support ticket. Shipping `mutate` to your test suite is how a real regression slips through disguised as a "successful" repair. The mode is an environment decision, not a personal preference — pick wrong in either direction and you've traded one failure mode for a worse one.
:::

## Quick Start: Wiring the Middleware

The validation battery exports two middleware factories from `@nhtio/adk/batteries/validation`:

1. `orderingGuardDispatchMiddleware` — Evaluates turn state on every dispatch iteration within the execution loop. Rejects violations via `ctx.nack(error)`.
2. `orderingGuardTurnMiddleware` — Evaluates turn state once before the execution loop begins. Halts violations via `ctx.abort(error)`.

```ts
import { TurnRunner } from '@nhtio/adk'
import { orderingGuardDispatchMiddleware } from '@nhtio/adk/batteries/validation'

const runner = new TurnRunner({
  executor: myExecutor,
  dispatchInputPipeline: [
    orderingGuardDispatchMiddleware({
      // String names resolve automatically from the built-in family recipe and atomic behavior catalogs
      profiles: ['anthropic-manual-thinking'],
      // 'mutate' automatically fixes repairable violations; 'enforce' strictly rejects
      action: 'mutate',
      onViolation: 'nack', // 'nack' rejects dispatch iteration; 'throw' raises an error
      onRepair: 'log', // 'log' logs warnings via console.warn; 'silent' suppresses logs
    }),
  ],
})
```

::: info Profile Resolution
Passing string profile names (such as `'anthropic-manual-thinking'` or `'strict_alternation'`) into `profiles: [...]` resolves them automatically from the built-in family recipe and atomic behavior registries.
:::

## The Two Modes: `enforce` vs `mutate`

```ts
export interface OrderingGuardOptions {
  profiles: (string | OrderingProfile)[]
  mode?: 'union-of-rules' | 'each' | 'first-match'
  action?: 'enforce' | 'mutate'
  onViolation?: 'nack' | 'throw'
  onRepair?: 'log' | 'silent'
  allowMetadataFallbackRepair?: boolean
  snapshotStashKey?: string
  disableAdvisoryRuleIds?: string[]
}
```

### 1. `action: 'enforce'` (Strict Paranoid Validation)

`enforce` is the default mode. It performs pure, read-only validation against your configured profiles, trusting nothing, repairing nothing, and treating every blocking rule violation as an immediate dispatch failure.

If any blocking violation is detected:

* In `orderingGuardDispatchMiddleware`, the iteration is rejected via `ctx.nack(error)` (or throws `E_ORDERING_VIOLATION` if `onViolation: 'throw'`).
* In `orderingGuardTurnMiddleware` (which has no `nack` capability), execution is halted via `ctx.abort(error)`.

No mutation of context primitives or timeline state occurs in `enforce` mode.

Use `enforce` mode when:

* Debugging pipeline transformations and verifying that upstream stages emit conformant history.
* Running CI conformance tests against custom executors or synthetic conversation traces.

### 2. `action: 'mutate'` (Best-Effort Auto-Repair)

`mutate` is the pragmatic mode: instead of dropping a live conversation on the floor over a sequence glitch, it actively repairs violations that have unambiguous, deterministic, and content-preserving fixes.

When violations are detected in `mutate` mode:

1. The guard classifies violations into repairable and unrepairable sets.
2. Safe repair strategies are applied to the timeline and context state.
3. The guard re-evaluates the repaired effective timeline.
4. If all blocking violations are resolved, execution continues (`next()`).
5. If unrepairable violations remain (e.g. lost historical context), the guard invokes `onViolation` (`nack`, `abort`, or `throw`) for the unrepaired subset.

Repairs applied during `mutate` mode are stored on `ctx.stash` under `__orderingGuardLastResult` (an `OrderingGuardResult` object) for downstream observability.

## Repair Strategies by Rule Type

This table is an honesty document. It spells out exactly which protocol violations this battery can safely paper over and which ones it flatly refuses to touch. That refusal is deliberate: silently fabricating a plausible-looking fix for lost conversation history or wedged primitives would be far worse than rejecting the dispatch outright.

| Rule Type                  | Strategy in `mutate` Mode   | Repairable? | Concrete Execution Behavior                                                                                                                                                                                                                                                                                           |
| -------------------------- | --------------------------- | :---------: | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `OrderRule`                | `reorder`                   |   **Yes**   | Shifts the offending primitive's `createdAt` (via the matching `ctx.mutate*`) so it sorts immediately before the primitive it must precede, then re-evaluates the repaired timeline to confirm the fix holds. This reaches the real turn state — an adapter's next history assembly sees the corrected order directly, nothing further to consume. |
| `AlternationRule`          | `insert-alternation-filler` |   **Yes**   | Inserts a synthetic `Message` role filler (with `id` and `content` set to `__ordering-guard-filler-<id1>-<id2>`) between consecutive same-role messages via `ctx.storeMessage`.                                                                                                                                       |
| `RequiredMetadataRule`     | `fill-required-metadata`    | **Opt-in**  | Fills missing vendor metadata (e.g. Gemini `thoughtSignature`) with a documented fallback sentinel. **Requires double opt-in**: both `action: 'mutate'` AND `allowMetadataFallbackRepair: true`.                                                                                                                      |
| `PreservationRule`         | None                        |   **No**    | **Never repairable.** When historical primitives or payload fields are dropped or altered upstream, the guard cannot invent lost context. Rejects dispatch.                                                                                                                                                           |
| `AdjacencyRule`            | None                        |   **No**    | **Never repairable.** Wedged or interleaving primitives cannot be safely dropped or reordered without risking causal intent. Rejects dispatch.                                                                                                                                                                        |
| `RoleRemapRule`            | N/A                         |     N/A     | Wire role remapping hints are adapter-level concerns, not pipeline ordering mutations.                                                                                                                                                                                                                                |
| `StaleContentAdvisoryRule` | N/A                         |     N/A     | **Never blocks.** Advisory rules produce informational notices only; no repair needed.                                                                                                                                                                                                                                |

## Caveats for Mutate Mode

When configuring `action: 'mutate'`, keep the following behavioral boundaries in mind:

1. **Scope of `OrderRule` Repairs:** When `OrderRule` repairs an ordering mismatch (such as placing thoughts before tool calls), the guard shifts the offending primitive's `createdAt` by calling the matching `ctx.mutateToolCall`/`mutateThought`/`mutateMessage` directly, then re-evaluates the repaired timeline to confirm the fix actually resolves the violation. Because this reaches the real `ctx.turnMessages`/`turnThoughts`/`turnToolCalls` state — not just an in-memory copy — any LLM adapter's own history assembly sees the corrected order on its next pass without reading anything from `ctx.stash`. `ctx.stash.get('__orderingGuardEffectiveTimeline')` remains available purely for observability, if you want to inspect exactly what the guard did on a given iteration.
2. **Metadata Fallback Provenance:** `action: 'mutate'` alone will not forge or fill missing vendor metadata. Fabricating a vendor signature is a direct provenance claim about where reasoning originated — it is the one place this battery could convincingly lie on your behalf. For that reason, missing metadata repair is never enabled by `mutate` alone; it requires an explicit, separate `allowMetadataFallbackRepair: true` opt-in. See [Gemini Thought Sentinels](./gemini-sentinels) for details.

## See Also

* **[Validation Hub](./index)** — Overview of the ordering guard battery and model lookup table.
* **[Atomic Behaviors](./behaviors)** — Catalog of the 16 atomic behavior profiles.
* **[Rule Types Reference](./rule-types)** — Specification of the seven declarative rule contracts.
* **[Gemini Sentinels](./gemini-sentinels)** — Using bypass sentinels and `allowMetadataFallbackRepair`.
