> ## Documentation Index
> Fetch the complete documentation index at: https://docs.qwedai.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Schema verifier

> The Schema Verifier combines Pydantic validation with embedded math constraints to enforce structural and numerical correctness.

The **Schema Verifier** goes beyond standard JSON validation. It combines **Pydantic** structure enforcement with **Symbolic Math** checks deeply embedded within the schema.

## How it works

It validates that:

1. **Structure:** The output matches the required JSON keys and types.
2. **Logic:** The numeric values *inside* the JSON are mathematically consistent (e.g., `total == sum(items)`).

## Usage

```python theme={null}
schema = {
    "type": "object",
    "properties": {
        "items": {"type": "array", "items": {"type": "number"}},
        "total": {"type": "number"}
    },
    # QWED Extension: Math Logic
    "qwed:constraints": [
        "total == sum(items)"
    ]
}

response = client.verify_schema(
    obj={"items": [10, 20], "total": 30},
    schema=schema
)
# -> ✅ Verified

response = client.verify_schema(
    obj={"items": [10, 20], "total": 300}, # LLM hallucinations
    schema=schema
)
# -> ❌ MATH ERROR: total (300) != sum(items) (30)
```

## Object validation

The Schema Verifier supports standard JSON Schema object keywords including `properties`, `required`, and `additionalProperties`.

### `additionalProperties: false` — strict fail-closed validation

When a schema sets `"additionalProperties": false` and the verifier runs with `strict=True` (the default), any property that is not declared in `properties` causes the payload to fail validation. The verifier records each undeclared property as an `ERROR`-severity `additional_property` issue, so `is_valid` is `false` and `status` is `INVALID`.

In non-strict mode (`strict=False`), `additionalProperties: false` is treated as advisory and undeclared properties do not block validation.

**Issue types returned for `additionalProperties`:**

| Issue type            | Severity         | Meaning                                                                  |
| --------------------- | ---------------- | ------------------------------------------------------------------------ |
| `additional_property` | `ERROR` (strict) | An undeclared property was present and `additionalProperties` is `false` |

**Example — strict mode rejects extra fields:**

```python theme={null}
schema = {
    "type": "object",
    "properties": {
        "name": {"type": "string"}
    },
    "required": ["name"],
    "additionalProperties": False
}

result = client.verify_schema(
    obj={"name": "rahul", "role": "admin"},
    schema=schema,
    strict=True
)
# -> is_valid: false, status: "INVALID"
# -> issue_type: "additional_property", severity: "ERROR"
# -> message: "Additional property 'role' not allowed"
```

**Example — declared-only payloads pass:**

```python theme={null}
result = client.verify_schema(
    obj={"name": "rahul"},
    schema=schema,
    strict=True
)
# -> is_valid: true, status: "VALID"
```

**Example — nested objects also fail closed:**

```python theme={null}
schema = {
    "type": "object",
    "properties": {
        "user": {
            "type": "object",
            "properties": {"name": {"type": "string"}},
            "required": ["name"],
            "additionalProperties": False
        }
    },
    "required": ["user"]
}

result = client.verify_schema(
    obj={"user": {"name": "rahul", "role": "admin"}},
    schema=schema,
    strict=True
)
# -> is_valid: false, status: "INVALID"
# -> path: "$.user.role", issue_type: "additional_property", severity: "ERROR"
```

<Info>
  This fail-closed behavior for strict `additionalProperties: false` was hardened in the v5.1.x line. See the [changelog](/changelog#qwed-verification--strict-additionalproperties-fail-closed) for the release notes.
</Info>

## Array validation

The Schema Verifier supports standard JSON Schema array keywords including `uniqueItems`.

### `uniqueItems` — fail-closed validation

When a schema sets `uniqueItems: true`, the verifier checks that every element in the array is distinct. If an element is unhashable or otherwise cannot be compared deterministically (for example, an object containing a Python `set`), the verifier **fails closed** — it reports a `uniqueness_validation_error` issue instead of silently passing.

This ensures that unverifiable arrays are never treated as valid.

**Issue types returned for `uniqueItems`:**

| Issue type                    | Meaning                                                          |
| ----------------------------- | ---------------------------------------------------------------- |
| `uniqueness_violation`        | Duplicate items were found in the array                          |
| `uniqueness_validation_error` | Uniqueness could not be verified deterministically (fail-closed) |

**Example — duplicate items:**

```python theme={null}
schema = {"type": "array", "uniqueItems": True}

result = client.verify_schema(obj=[1, 2, 2, 3], schema=schema)
# -> is_valid: false, issue_type: "uniqueness_violation"
```

**Example — uncheckable items fail closed:**

```python theme={null}
schema = {"type": "array", "uniqueItems": True}

# Items containing unhashable types cannot be compared deterministically
result = client.verify_schema(obj=[{"bad": {1, 2}}, {"bad": {3, 4}}], schema=schema)
# -> is_valid: false, status: "INVALID"
# -> issue_type: "uniqueness_validation_error"
# -> message: "uniqueItems could not be verified deterministically: ..."
```

<Info>
  This fail-closed behavior shipped in v5.1.0. See the [changelog](/changelog#v5-1-0-—-agent-state-governance-and-fail-closed-hardening) for the full release notes.
</Info>

## Object validation

### Strict mode and `additionalProperties: false`

When a schema sets `additionalProperties: false` and you call the verifier with `strict=True`, undeclared properties on the payload now cause verification to **fail closed**. The verifier emits an `additional_property` issue with severity `ERROR`, and the overall result is `INVALID`.

Previously, undeclared properties in strict mode were recorded with severity `WARNING`. Because validity was determined only by `ERROR`-level issues, payloads with forbidden extra fields could still return `VALID`. A strict schema denial is a structural validation failure, not an advisory warning, so strict mode now rejects these payloads outright.

This applies at every level of the schema — extra properties on nested objects that set `additionalProperties: false` are also rejected.

**Behavior matrix:**

| Mode           | `additionalProperties` | Extra field present | Result                                                      |
| -------------- | ---------------------- | ------------------- | ----------------------------------------------------------- |
| `strict=True`  | `false`                | yes                 | `is_valid: false`, `status: "INVALID"`, `severity: "ERROR"` |
| `strict=True`  | `false`                | no                  | `is_valid: true`, `status: "VALID"`                         |
| `strict=False` | `false`                | yes                 | `is_valid: true`, `status: "VALID"` (permissive)            |

**Example — strict mode rejects extra fields:**

```python theme={null}
schema = {
    "type": "object",
    "properties": {
        "name": {"type": "string"}
    },
    "required": ["name"],
    "additionalProperties": False,
}

result = verifier.verify(
    {"name": "rahul", "role": "admin"},
    schema,
    strict=True,
)
# -> is_valid: false, status: "INVALID"
# -> issue: { "type": "additional_property", "severity": "ERROR",
#             "message": "Additional property 'role' not allowed" }
```

**Example — nested objects also fail closed:**

```python theme={null}
schema = {
    "type": "object",
    "properties": {
        "user": {
            "type": "object",
            "properties": {"name": {"type": "string"}},
            "required": ["name"],
            "additionalProperties": False,
        }
    },
    "required": ["user"],
}

result = verifier.verify(
    {"user": {"name": "rahul", "role": "admin"}},
    schema,
    strict=True,
)
# -> is_valid: false
# -> issue path: "$.user.role"
```

<Info>
  This fail-closed behavior is enabled only when `strict=True`. With `strict=False`, extra properties remain non-blocking and the payload is still treated as valid, preserving permissive workflows.
</Info>

## When to use

* **Invoice Processing:** Ensure line items sum to the total.
* **Financial Reports:** Ensure balance sheets balance.
* **Tax Forms:** Ensure calculated fields match underlying data.
* **Strict API contracts:** Reject payloads with undeclared fields when `strict=True` and `additionalProperties: false` are combined.
