Declarative Rule Types Reference
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.
export type OrderingRule =
| OrderRule
| RequiredMetadataRule
| AlternationRule
| AdjacencyRule
| PreservationRule
| RoleRemapRule
| StaleContentAdvisoryRule1. OrderRule (type: 'order')
Enforces relative ordering between two primitive categories (before and after).
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: Whentrue, 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.
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 invalue.payload(e.g.'thoughtSignature').severity:'blocking'(default) halts dispatch on violation;'advisory'records an informational finding without halting.fallbackPayloadValue/fallbackReplayCompatibility: Documented sentinel values used bymutatemode when metadata fallback repair is enabled.
3. AlternationRule (type: 'alternation')
Enforces strict role cycling across conversation turns.
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 onToolCallprimitives within a single assistant role group (e.g.1for 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.
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 afterfirst.
5. PreservationRule (type: 'preservation')
A stateful check that diffs the current dispatch timeline against the previous iteration's snapshot on ctx.stash.
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 ofkindnever decreases.payload-field-stable: Enforces that the value atpayloadFieldremains 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).
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 intovalue.payloadexpected to equalvariant.
7. StaleContentAdvisoryRule (type: 'staleContentAdvisory')
Non-blocking hygiene recommendation rule that checks for obsolete content without blocking dispatch.
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 toOrderingGuardOptions.disableAdvisoryRuleIds.
See Also
- Validation Hub — Overview of the ordering guard battery.
- Atomic Behaviors — Catalog of atomic profiles built from these rule types.
- Operating Modes — How each rule type behaves in
enforcevsmutatemode. - Writing a Profile — Step-by-step guide to writing rules and profiles.