Skip to main content
New in v5.1.0
AgentStateGuard verifies proposed agent state payloads deterministically before any side effects occur. It enforces strict JSON parsing, schema validation, and configurable transition rules to prevent agents from corrupting their own state.

When to use AgentStateGuard

Use AgentStateGuard when your AI agents maintain structured state (such as task lists, workflow progress, or configuration) and you need guarantees that:
  • State payloads conform to a strict schema before they are persisted
  • State transitions follow monotonic, immutable, or ordered-enum constraints
  • Writes to disk are atomic — either fully committed or not written at all
AgentStateGuard was introduced in v5.1.0. It operates entirely in-process, requires no network access or API key, and all verification is deterministic and fail-closed.

How it works

AgentStateGuard uses a three-phase approach:
  1. Structural verification (Phase 1) — Validates that a proposed JSON state payload conforms to a strict schema. Rejects duplicate keys, non-standard JSON constants (NaN, Infinity), and unexpected fields. Numbers are parsed as Decimal to preserve deterministic numeric semantics.
  2. Semantic transition verification (Phase 2) — Verifies that a proposed state transition satisfies configured rules. Immutable paths cannot change, integer paths must increase monotonically, enum paths must advance forward, and keyed arrays must preserve order.
  3. Governed atomic commit (Phase 3) — After verification passes, writes the normalized state to disk atomically using tempfile + os.replace. The write target must be within configured allowed roots and must end in .json.
All three phases are fail-closed — if any check fails, no side effects occur.

Canonicalization and Unicode normalization

Before validation, AgentStateGuard canonicalizes every parsed payload: dict keys are sorted and all string keys and values are normalized to Unicode Normalization Form C (NFC). Precomposed and decomposed spellings of the same text (for example "\u00C9" vs "E\u0301") therefore reduce to the same canonical bytes.
Equivalent Unicode text produces identical verification results. The same payload written with precomposed or decomposed characters yields the same normalized_state and the same proof_ref.
Schema property names, required lists, and enum values are canonicalized the same way at construction time, so a payload key spelled "café" matches a schema property spelled "cafe\u0301". If two distinct keys in the same object collide under NFC, construction (or verification) is rejected with QWED-AGENT-STATE-102 rather than silently dropping data.

Proof references

Every VERIFIED result includes a proof_ref — the SHA-256 hex digest of the canonical evidence payload produced during verification. Because the digest is computed from the canonicalized (NFC-normalized, key-sorted) form, it is stable across equivalent inputs and reproducible by any consumer that recanonicalizes the same data.
  • verify_state_payloadproof_ref covers {"normalized_state": ...}.
  • verify_state_transitionproof_ref covers {"normalized_previous_state": ..., "normalized_state": ...}.
  • verify_transition_and_commit_stateproof_ref is bound to the exact bytes written to disk (the canonical serialization of normalized_state), so it can be recomputed from the committed file. The separate transition_proof_ref covers the transition evidence.
The human-readable sentence that older releases returned as proof now lives in developer_fields.proof_reason and is intended for logs and dashboards. The verified boolean and all method signatures are unchanged.

Usage

Phase 1: structural verification

Phase 2: transition verification

Phase 3: atomic commit

The target_path must be an absolute path ending in .json, and its parent directory must already exist. The path must fall within one of the configured allowed_commit_roots.

API reference

AgentStateGuard(required_schema, transition_rules, allowed_commit_roots)

Creates a new AgentStateGuard instance. The schema and transition rules are frozen on construction — later mutations to the original dicts have no effect.
dict
required
A strict JSON schema definition. Must include a type field (object, array, string, integer, number, boolean, or null). Object schemas must define properties and may include required and additionalProperties (boolean). Array schemas must define items. Enum constraints use the enum key with a non-empty list.
dict | None
default:"None"
Semantic transition rules. At least one rule must have a non-empty value for transition verification to be enabled. Supported keys are described in the transition rules section.
list[str] | None
default:"None"
List of absolute directory paths where atomic commits are permitted. Required for verify_transition_and_commit_state. Each entry must be an absolute path string.

verify_state_payload(proposed_state_json)

Validates a proposed state payload against the configured schema.
str
required
A JSON string representing the proposed agent state. Must be a non-empty string containing valid JSON.
Returns a decision object:

verify_state_transition(current_state_json, proposed_state_json)

Validates a state transition against both structural and semantic rules.
str
required
JSON string representing the current agent state.
str
required
JSON string representing the proposed new agent state.
Returns a decision object with the same fields as verify_state_payload, plus:

verify_transition_and_commit_state(current_state_json, proposed_state_json, target_path)

Verifies the transition and atomically writes the normalized state to disk if verification passes.
str
required
JSON string representing the current agent state.
str
required
JSON string representing the proposed new agent state.
str
required
Absolute path to the target .json file. The parent directory must exist, and the path must fall within a configured allowed_commit_roots directory.
Returns a decision object with the same fields as verify_state_transition, plus: The top-level proof_ref on this method is bound to the exact bytes written to disk — the SHA-256 of the canonical serialization of normalized_state. Any consumer with access to the committed file can recompute and verify it without replaying the transition.

Transition rules

Transition rules define semantic constraints that must hold between the current and proposed state. All paths use dot-style JSON path notation starting with $..

immutable_paths

A list of paths whose values must not change between states.

monotonic_integer_paths

A list of paths whose integer values must never decrease.

ordered_enum_paths

A dictionary mapping paths to ordered lists of allowed values. The value at each path must advance forward (or stay the same) in the list — it cannot move backward.

keyed_object_array_paths

A dictionary mapping array paths to rules for keyed object arrays. Each rule specifies: Existing items must preserve their order and cannot be removed. All non-boolean fields on existing items are immutable.

Error codes

Security considerations

  • Strict JSON parsing: Duplicate keys and non-standard constants (NaN, Infinity, -Infinity) are rejected. Numbers are parsed as Decimal to avoid floating-point non-determinism.
  • Unicode canonicalization: All string keys and values are normalized to NFC before validation, so precomposed and decomposed spellings of the same text cannot smuggle divergent hashes past the guard. Objects containing two keys that collide under NFC are rejected rather than silently merged.
  • Reproducible proof references: proof_ref is a SHA-256 over the canonical evidence payload, so any consumer can independently recompute and verify it. For committed state, proof_ref is bound to the exact bytes on disk.
  • Frozen configuration: Schemas and transition rules are deeply frozen on construction using MappingProxyType and tuples. Callers cannot mutate the guard’s configuration after initialization.
  • Fail-closed design: Every method returns a BLOCKED decision object on failure — no exceptions leak past the public API unless the constructor arguments themselves are invalid.
  • Path traversal prevention: Commit targets are resolved to absolute paths and validated against the allowed_commit_roots allowlist. Only .json file extensions are permitted.
  • Atomic writes: State files are written via tempfile.NamedTemporaryFile followed by os.replace, which is atomic on POSIX systems. The temporary file is cleaned up even if the rename fails.
  • Depth limit: Schema validation enforces a maximum recursion depth of 64 to prevent stack overflow from deeply nested payloads.

Next steps

StateGuard

Workspace rollback using shadow git snapshots

Agent verification

Pre-execution verification for AI agents

SDK guards

All available security guards

Attestations

Cryptographic proof of verification