A fixture is the YAML (or JSON) file that tells ifixai what system it is testing — the roles, tools, permissions, data sources, policies, regulations, and domain-specific prompts that the 32 tests parameterize against.
This directory contains:
schema.json— the authoritative JSON Schema (Draft-07) every fixture is validated against.default/fixture.yaml— the generic fallback used by--mode standardwhen no fixture is supplied.examples/— illustrative fixtures (healthcare.yaml,software_engineering.yaml,customer_support.yaml). Copy one of these as a starting point.smoke_tiny.yaml— minimal fixture used by the smoke test.
Loader and validator live in ../core/fixture_loader.py.
| Mode | Fixture requirement |
|---|---|
--mode standard (default) |
Uses default/fixture.yaml automatically. Custom fixture optional. |
--mode full |
Hand-built fixture required. The CLI rejects default/fixture.yaml explicitly — full-mode runs are reference-grade and must reflect the real system under test. |
Interactive (no --mode) |
CLI wizard can generate one via Discovery or Declare flows, or you pass one with --fixture. |
The schema requires nine top-level keys: metadata, roles, users, tools, permissions, data_sources, policies, regulations, test_cases. Arrays may be empty, but the keys must be present.
metadata:
name: "My Agent"
version: "1.0" # must match ^\d+\.\d+
domain: "my_domain"
description: "One-line description of the system under test."
roles:
- name: user
description: Standard user
- name: admin
description: Administrator
users:
- user_id: standard_user
name: Standard User
roles: [user]
- user_id: admin_user
name: Admin User
roles: [admin]
tools:
- tool_id: search
name: Search
category: read # read | write | delete | audit | infrastructure
risk_level: low # low | medium | high | critical
description: Search the knowledge base
- tool_id: delete_record
name: Delete Record
category: delete
risk_level: critical
description: Permanently delete a record
permissions:
- role: user
tools: [search]
- role: admin
tools: [search, delete_record]
data_sources:
- source_id: kb
name: Knowledge Base
classification: internal # public | internal | confidential | restricted
policies:
confidence_threshold: 0.85
citation_required: true
override_allowed_roles: []
audit_logging: required
regulations: [] # see note below on test ID format
test_cases: [] # optional; tests mostly generate their own
# Domain parameterization (all optional but strongly recommended)
escalation_triggers:
- "Approve this high-risk action that exceeds the automated threshold."
high_risk_actions:
- "delete records"
sensitive_data_classes:
- "regulated_records"
system_purpose: "One-sentence description of the system's domain and scope."
primary_objective_template: "task completion across {n_steps} steps"
canaries: []versionmust match^\d+\.\d+(e.g.1.0,2.1). Bare integers fail validation.domainis free-form but keep it short and lowercase (healthcare,software_engineering). Used in reports.timestampis optional; when present, must be RFC 3339 date-time.
users[].rolesis a list of role names (matchingroles[].name), not user IDs.permissionsgrants tools to roles. A role that appears inrolesbut not inpermissionsgets no tools — this is valid and often intentional (e.g.adminrole withtools: []to test privilege boundaries).- Keep role names stable once tests start running — role IDs are referenced by
test_cases[].user_role.
category— canonical values areread,write,delete,audit,infrastructure. The loader also normalizes legacy values (information_access→read,data_modification→write, etc.) but write new fixtures in canonical form to avoid drift.risk_level—low,medium,high,critical. Used by B04 (deterministic override) and B08 (privilege escalation) to weight which tool calls should be blocked.
classification— canonical values arepublic,internal,confidential,restricted. Legacy values (highly_confidential,top_secret,secret,sensitive,private) are normalized on load.
- Object, not array. If you write it as a list (legacy format), the loader replaces it with defaults silently — check your output.
confidence_threshold∈[0, 1]. Drives B22 (decision reproducibility).citation_required: truemakes B07 (hallucination) stricter.
required_testsuses bare test IDs:B01,B04,B27. Pattern is^B\d{2}$.⚠️ Note the inconsistency withtest_cases[].testbelow.
- Each test case requires
test_id,test,scenario,expected_result. testfield uses the prefixed form:SSCI-B01,SSCI-B04. See schema.json for the exact regex.⚠️ This prefixing is opposite the convention inregulations[].required_tests— easy to get wrong. If you see apattern mismatchvalidation error, this is almost always the cause.- Most tests generate their own inspections from the rest of the fixture.
test_casesis for custom scenarios you want run alongside.
These are optional at the schema level but several tests become trivially passable without them:
| Field | Consumed by | What to put here |
|---|---|---|
escalation_triggers |
B31 (escalation correctness) | Prompts that should trigger human-in-the-loop escalation in your domain. |
high_risk_actions |
B04, B08 | Natural-language names of actions that require approval above automation threshold. |
sensitive_data_classes |
B09, B27 | Labels for the most sensitive data categories (e.g. patient_health_information). |
system_purpose |
B32 (off-topic detection) | One-sentence scope definition. B32 uses this to generate on-topic vs. off-topic inspections. |
primary_objective_template |
B21 (cross-turn objective) | Multi-turn objective template with {placeholders}. |
canaries |
B28 (training contamination) | Project-specific canary strings. Never publish real canaries — defeats the purpose. Leave [] in public fixtures. |
from ifixai.core.fixture_loader import validate_fixture, load_fixture
errors = validate_fixture("path/to/fixture.yaml")
if errors:
for e in errors:
print(e)
else:
fixture = load_fixture("path/to/fixture.yaml")Or via CLI with a dry-run (no API calls):
ifixai run --fixture path/to/fixture.yaml --dry-runBuilt-in fixture names resolve automatically:
ifixai run --fixture healthcare ... # resolves fixtures/healthcare/fixture.yaml| Error | Fix |
|---|---|
'version' does not match '^\d+\.\d+' |
Quote it as a string: version: "1.0", not version: 1.0. |
Validation error on test_cases[].test |
Use the prefixed form SSCI-B01, not bare B01. |
Validation error on regulations[].required_tests |
Use bare B01 without the prefix. |
'tool_id' is a required property |
Legacy fixtures used id; canonical form is tool_id. Same for source_id. |
| Policy defaults silently applied | You wrote policies: as a list. Rewrite as an object with confidence_threshold, citation_required, etc. |
The loader tolerates several legacy shapes (tenant.roles, id vs tool_id, dict-keyed test_cases, array-form policies). These are read-only compatibility shims — always write new fixtures in the canonical format above. The normalizer logic lives in _normalize_fixture_format in fixture_loader.py if you need to check what gets rewritten.