---
url: 'https://adk.nht.io/batteries/validation/rule-types.md'
description: >-
  Specification of the seven declarative ordering rule contracts in the
  validation battery: OrderRule, RequiredMetadataRule, AlternationRule,
  AdjacencyRule, PreservationRule, RoleRemapRule, and StaleContentAdvisoryRule.
---

# Declarative Rule Types Reference

## LLM summary — Declarative Rule Types Reference

* All atomic ordering profiles compile into one or more of seven discriminated union variants in `OrderingRule`.
* The seven typed rule contracts:
  * `OrderRule` (`type: 'order'`): Relative order between primitive kinds (`before`, `after`, `scope`, `onlyLatestGroup`).
  * `RequiredMetadataRule` (`type: 'requiredMetadata'`): Requires metadata key on primitive payloads (`kind`, `applyTo`, `requiredPayloadKey`, `severity`, `fallbackPayloadValue`, `fallbackReplayCompatibility`).
  * `AlternationRule` (`type: 'alternation'`): Enforces role cycling (`roles`, `mode: 'strict'`, `maxPerGroup`).
  * `AdjacencyRule` (`type: 'adjacency'`): Constrains immediate successors (`first`, `disallowBetween`). No `second` or `correlateBy` fields.
  * `PreservationRule` (`type: 'preservation'`): Stateful continuity invariants (`kind`, `invariant: 'count-non-decreasing' | 'payload-field-stable' | 'pruned-after-latest-turn'`, `payloadField`, `resetOnModelSwitch`).
  * `RoleRemapRule` (`type: 'roleRemap'`): Wire-role remapping validation (`kind`, `variant`, `expectedRoleTag`).
  * `StaleContentAdvisoryRule` (`type: 'staleContentAdvisory'`): Non-blocking hygiene notices (`kind`, `scope: 'before-latest-user-turn'`, `optOutOptionKey`).

Every atomic profile in the validation battery is composed of one or more declarative rule objects. The battery supports seven typed rule contracts exported from `@nhtio/adk/batteries/validation`.

```ts
export type OrderingRule =
  | OrderRule
  | RequiredMetadataRule
  | AlternationRule
  | AdjacencyRule
  | PreservationRule
  | RoleRemapRule
  | StaleContentAdvisoryRule
```

***

## 1. `OrderRule` (`type: 'order'`)

Enforces relative ordering between two primitive categories (`before` and `after`).

```ts
export interface OrderRule {
  type: 'order'
  id: string
  before: 'message' | 'thought' | 'toolCall'
  after: 'message' | 'thought' | 'toolCall'
  scope: 'adjacent-same-role-group' | 'entire-turn'
  onlyLatestGroup?: boolean
}
```

* **`before` / `after`**: Defines the required relative order.
* **`scope`**: When `'adjacent-same-role-group'`, ordering is checked within contiguous role segments. When `'entire-turn'`, ordering spans the full dispatch timeline.
* **`onlyLatestGroup`**: When `true`, ignores older historical groups and checks only the active turn (e.g. Anthropic's manual thinking rule).

***

## 2. `RequiredMetadataRule` (`type: 'requiredMetadata'`)

Enforces that a primitive carries specific provider metadata in its `payload` object.

```ts
export interface RequiredMetadataRule {
  type: 'requiredMetadata'
  id: string
  kind: 'message' | 'thought' | 'toolCall'
  applyTo: 'first-in-group' | 'every'
  requiredPayloadKey: string
  severity?: 'blocking' | 'advisory'
  gatedByReplayCompatibility?: string[]
  fallbackPayloadValue?: unknown
  fallbackReplayCompatibility?: string
}
```

* **`applyTo`**: `'first-in-group'` targets only the leading primitive in an assistant group; `'every'` checks all matching primitives.
* **`requiredPayloadKey`**: Dot-path in `value.payload` (e.g. `'thoughtSignature'`).
* **`severity`**: `'blocking'` (default) halts dispatch on violation; `'advisory'` records an informational finding without halting.
* **`fallbackPayloadValue` / `fallbackReplayCompatibility`**: Documented sentinel values used by `mutate` mode when metadata fallback repair is enabled.

***

## 3. `AlternationRule` (`type: 'alternation'`)

Enforces strict role cycling across conversation turns.

```ts
export interface AlternationRule {
  type: 'alternation'
  id: string
  roles: ReadonlyArray<'user' | 'assistant'>
  mode: 'strict'
  maxPerGroup?: number
}
```

* **`roles`**: Permitted role alternation sequence (normally `['user', 'assistant']`).
* **`mode`**: `'strict'` requires every consecutive turn to alternate roles.
* **`maxPerGroup`**: Optional upper bound on `ToolCall` primitives within a single assistant role group (e.g. `1` for Llama 3).

***

## 4. `AdjacencyRule` (`type: 'adjacency'`)

Constrains the immediate successor of a primitive. In this ADK, tool execution results are stored directly on `ToolCall` rather than on separate message payloads, so adjacency rules directly forbid disallowed primitive kinds from appearing immediately after a specified primitive.

```ts
export interface AdjacencyRule {
  type: 'adjacency'
  id: string
  first: 'message' | 'thought' | 'toolCall'
  disallowBetween: Array<'message' | 'thought' | 'toolCall'>
}
```

* **`first`**: The primitive kind whose immediate successor is restricted.
* **`disallowBetween`**: Array of primitive kinds that are forbidden from appearing immediately after `first`.

***

## 5. `PreservationRule` (`type: 'preservation'`)

A stateful check that diffs the current dispatch timeline against the previous iteration's snapshot on `ctx.stash`.

```ts
export interface PreservationRule {
  type: 'preservation'
  id: string
  kind: 'message' | 'thought' | 'toolCall'
  invariant:
    | 'count-non-decreasing'
    | 'payload-field-stable'
    | 'pruned-after-latest-turn'
  payloadField?: string
  resetOnModelSwitch?: boolean
}
```

* **`count-non-decreasing`**: Enforces that the total count of historical primitives of `kind` never decreases.
* **`payload-field-stable`**: Enforces that the value at `payloadField` remains unchanged (compared as JSON) across iterations.
* **`pruned-after-latest-turn`**: Allows primitives older than the latest non-tool-call user message to be pruned, while requiring all primitives at or after that boundary to remain present and stable.

***

## 6. `RoleRemapRule` (`type: 'roleRemap'`)

Describes required provider-specific wire role tags for model families with custom role schemas (such as IBM Granite).

```ts
export interface RoleRemapRule {
  type: 'roleRemap'
  id: string
  kind: 'message' | 'thought' | 'toolCall'
  variant: string
  expectedRoleTag: string
}
```

* **`variant`**: Profile-defined mapping identifier the producer is expected to have set (e.g. `'granite-3.x'`).
* **`expectedRoleTag`**: Dot-path into `value.payload` expected to equal `variant`.

***

## 7. `StaleContentAdvisoryRule` (`type: 'staleContentAdvisory'`)

Non-blocking hygiene recommendation rule that checks for obsolete content without blocking dispatch.

```ts
export interface StaleContentAdvisoryRule {
  type: 'staleContentAdvisory'
  id: string
  kind: 'message' | 'thought' | 'toolCall'
  scope: 'before-latest-user-turn'
  optOutOptionKey: string
}
```

* **`scope`**: Identifies content predating the latest user turn.
* **`optOutOptionKey`**: Identifies the corresponding configuration option (e.g. `'preserveThinking'`) and maps to [`OrderingGuardOptions.disableAdvisoryRuleIds`](https://adk.nht.io/api/@nhtio/adk/batteries/validation/types/interfaces/OrderingGuardOptions#property-disableadvisoryruleids).

***

## See Also

* **[Validation Hub](./index)** — Overview of the ordering guard battery.
* **[Atomic Behaviors](./behaviors)** — Catalog of atomic profiles built from these rule types.
* **[Operating Modes](./modes)** — How each rule type behaves in `enforce` vs `mutate` mode.
* **[Writing a Profile](./writing-a-profile)** — Step-by-step guide to writing rules and profiles.
