JSON Schema 2020-12 & Validation Pipeline
iris intermediate 5 min read
ELI5
JSON Schema is like a strict teacher who checks your homework against a detailed answer key. iris.schema.json is the answer key for IRIS documents. It says exactly what fields are required, what patterns names must follow, and how everything fits together. The validation pipeline is the teacher’s red pen — it marks every mistake so you can fix it before submitting.
Technical Deep Dive
Schema Overview
flowchart TD A["iris.schema.json<br/>JSON Schema Draft 2020-12"] --> B["$defs: Shared types"] A --> C["oneOf: Top-level discriminator"] C --> D["Sprite"] C --> E["Council"] C --> F["Chain"] B --> G["UUID, Semver, SpriteName, HexHash"] B --> H["Capability, TestCase, ChainStep"] B --> I["Gate, Rule, Fingerprint, SpriteMetadata"]Top-Level Discriminator
A valid IRIS document must be exactly one of three types, discriminated via oneOf:
| Type | Discriminating Field | Required Fields |
|---|---|---|
| Sprite | system_prompt | id, name, version, capabilities, system_prompt, metadata, fingerprint |
| Council | sprites | id, name, domain, sprites, chains, gate_agents, rules |
| Chain | steps | id, name, steps, gates, timeout |
$defs: Shared Type Definitions
classDiagram class SharedDefs["$defs"] { +UUID +Semver +SpriteName +HexHash +Duration +Fingerprint +SpriteMetadata +Capability +TestCase +ChainStep +Gate +Rule }| Type | Pattern / Format | Example |
|---|---|---|
| UUID | ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$ | 550e8400-e29b-41d4-a716-446655440000 |
| Semver | ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([...]))?(?:\+([...]))?$ | 1.0.0, 2.1.0-alpha+build.123 |
| SpriteName | ^[A-Z][A-Z0-9]*(-[A-Z0-9]+)*$ | SOL-FORGE, CORTEX-01 |
| HexHash | ^[0-9a-f]{64}$ | a1b2c3d4... (64 chars) |
| Duration | ^([1-9]\d*)(ms|s|m|h)$ | 30s, 5m, 1h |
Validation Pipeline
flowchart TD A["Input Document"] --> B{"Format?"} B -->|YAML| C["PyYAML parse"] B -->|JSON| D["json.loads"] C --> E["Python dict"] D --> E E --> F["jsonschema.Draft202012Validator"] F --> G{"$ref resolution"} G --> H["Resolve against #/$defs/*"] H --> I["Validate oneOf discriminator"] I --> J{"Sprite?"} I --> K{"Council?"} I --> L{"Chain?"} J --> M["Validate Sprite schema"] K --> N["Validate Council schema"] L --> O["Validate Chain schema"] M --> P{"Valid?"} N --> P O --> P P -->|Yes| Q["Return: valid=true"] P -->|No| R["Return: errors[]"]Validation in Python SDK
from iris.sprite import Sprite
# Strict validationsprite = Sprite.from_file("sprite.yaml")is_valid = sprite.validate(strict=True)
# Lenient validation (auto-converts string capabilities, strips extra fields)is_valid = sprite.validate(strict=False)Lenient mode behaviour:
- String capabilities are auto-converted to capability objects
testsare excluded from validation- Extra fields not in the schema are stripped
Validation in MCP Server
The validate_sprite MCP tool performs client-side field-by-field validation:
function validateSprite(sprite): ValidationResult { const errors: ValidationError[] = [];
// Name: required, matches SOL-[A-Z]+-\d+ if (!sprite.name || !/^SOL-[A-Z]+-\d+$/.test(sprite.name)) { errors.push({ field: "$.name", message: "Invalid sprite name", severity: "error" }); }
// Version: required, semver if (!sprite.version || !/^\d+\.\d+\.\d+$/.test(sprite.version)) { errors.push({ field: "$.version", message: "Invalid version", severity: "error" }); }
// Role: one of 6 valid roles const validRoles = ["architect", "reviewer", "documenter", "operator", "test-architect", "planner"]; if (!validRoles.includes(sprite.role)) { errors.push({ field: "$.role", message: "Invalid role", severity: "error" }); }
// Capabilities: array, min 1, each has name + description if (!Array.isArray(sprite.capabilities) || sprite.capabilities.length < 1) { errors.push({ field: "$.capabilities", message: "At least 1 capability required", severity: "error" }); }
// System prompt: required, non-empty string if (!sprite.system_prompt || typeof sprite.system_prompt !== "string" || sprite.system_prompt.length === 0) { errors.push({ field: "$.system_prompt", message: "System prompt required", severity: "error" }); }
return { valid: errors.filter(e => e.severity === "error").length === 0, errors, schema_version: "1.0.0" };}Schema Change Process
Per GOVERNANCE.md, schema changes follow a 7-step process:
- Propose via PR to
iris-specs - Update
iris.schema.json - Update all affected example sprites
- Run validation scripts against all examples
- Update
CHANGELOG.md - Maintainer review + approval
- Downstream repos update their schema dependency
Key Terms
- JSON Schema Draft 2020-12 → The specification version used by IRIS; supports
oneOf,$defs, and advanced validation - $defs → A schema section defining reusable types (UUID, Semver, SpriteName, etc.)
- oneOf discriminator → Ensures a document matches exactly one of Sprite, Council, or Chain
- $ref resolution → Referencing shared type definitions within the schema (e.g.,
$ref: "#/$defs/Capability") - Lenient validation → SDK mode that auto-fixes minor issues (string→object conversion) for developer convenience
- Schema propagation → The process of updating downstream repos when
iris.schema.jsonchanges
Q&A
Q: Can I add custom fields to a sprite?
A: The JSON Schema sets additionalProperties: false on Sprite, Council, and Chain. Extra fields are rejected in strict mode and stripped in lenient mode.
Q: What happens if a $ref can’t be resolved?
A: jsonschema raises a RefResolutionError. The SchemaLoader loads the full schema with all $defs to prevent this.
Q: How does the schema enforce unique capabilities?
A: The capabilities array uses uniqueItems: true. Duplicate capability names within a sprite are rejected.
Q: Can I use a different JSON Schema draft?
A: No. IRIS standardises on Draft 2020-12. Using a different draft would break $ref resolution and validation behaviour.
Q: What is the relationship between iris.schema.json and iris-api.openapi.yaml?
A: iris.schema.json defines the data model. iris-api.openapi.yaml defines the REST API that operates on those models. Both are contracts consumed by SDKs and services.
Examples
JSON Schema validation is like a passport control system:
- UUID regex = The machine-readable zone format (exactly 36 characters, specific dash positions)
- SpriteName regex = The country code rules (must be 2–3 uppercase letters)
- Semver regex = The passport number format (must follow a specific pattern)
- oneOf discriminator = The gate that determines if you’re a citizen, resident, or visitor — you must be exactly one
- $defs = The shared reference book all border agents use (same definitions everywhere)
- Validation errors = The reasons you’re sent to secondary inspection (photo doesn’t match, visa expired, wrong document type)
- Lenient mode = The helpful agent who says “your photo is a bit dark, but I can still verify it’s you” instead of sending you away
neighbors on the map
- 5 Locked Architectural Decisions (DEC-001 to DEC-005) understanding why IRIS is built the way it is
- In-Memory Registry Architecture understanding how iris-service stores data