"""Tests for schema validation — at least one valid example fixture per schema."""

from __future__ import annotations

import pytest

from abi_control_core.sdk.validate import SchemaValidationError, validate_json

# ── Valid fixtures for each mirrored template schema ───────────────────────

VALID_RUN_MANIFEST = {
    "schema_version": "run-manifest/1.0",
    "run_id": "abcd1234",
    "gate": "intent-gate",
    "mode": "exploration",
    "trigger": "manual",
    "started_at": "2026-02-25T10:00:00Z",
    "completed_at": "2026-02-25T10:05:00Z",
    "intent_gate_hash": "sha256:" + "a" * 64,
    "verdict": "pass",
    "summary": {"total": 5, "passed": 5, "failed": 0, "skipped": 0, "blocker": 0},
}

VALID_BUDGET_RECEIPT = {
    "schema_version": "budget-receipt/1.0",
    "run_id": "abcd1234",
    "gate": "intent-gate",
    "generated_at": "2026-02-25T10:05:00Z",
    "budget_policy": {"enforcement_mode": "warn", "total_planned": 1000, "total_max": 2000},
    "steps": [
        {
            "step_id": "s1",
            "planned": 500,
            "reserved": 600,
            "actual": 450,
            "released": 150,
            "status": "within_budget",
        }
    ],
    "summary": {
        "total_planned": 1000,
        "total_reserved": 1200,
        "total_actual": 900,
        "total_released": 300,
        "breaches": 0,
        "verdict": "within_budget",
    },
}

VALID_SPAN = {
    "span_id": "aabb0011",
    "name": "schema_validate",
    "start_time": "2026-02-25T10:00:00Z",
    "end_time": "2026-02-25T10:00:01Z",
    "status": "ok",
}

VALID_TRACE_EVENT = {
    "ts": "2026-02-25T10:00:00Z",
    "event_type": "gate_start",
    "run_id": "abcd1234",
    "status": "start",
}

VALID_CONTEXT_MANIFEST = {
    "schema_version": "context-manifest/1.0",
    "repo_id": "my-repo",
    "created_at": "2026-01-01T00:00:00Z",
    "updated_at": "2026-02-25T00:00:00Z",
    "owners": ["@dev"],
    "sensitivity": "internal",
    "context_sets": [
        {
            "id": "agent-bootstrap",
            "description": "Core context for agent startup.",
            "path": "context/agent-bootstrap",
            "intended_use": "agent_bootstrap",
            "max_bytes": 102400,
            "freshness_days": 30,
        }
    ],
    "policies": {
        "disallowed_patterns": ["password\\s*="],
        "disallowed_file_globs": ["*.env"],
        "allowlist_file_globs": ["*.md", "*.yaml"],
        "hash_algo": "sha256",
    },
}

VALID_CRP = {
    "schema": "crp/1.0",
    "crp_id": "crp-test-1",
    "plan_id": "plan-test-1",
    "phase": "phase2_parallel",
    "template_version": "v0.20.0",
    "completed_at": "2026-02-25T10:00:00Z",
    "verdict": "pass",
    "lanes_completed": [
        {"lane_id": "lane_a", "name": "Lane A", "status": "complete", "files_created": ["a.py"]}
    ],
    "waves_completed": [{"wave": 1, "name": "Planning", "status": "complete"}],
    "evidence": {
        "intent_gate_hash": "sha256:" + "b" * 64,
        "gate_run_id": "abcd1234",
        "gate_verdict": "pass",
    },
    "gate_results": {"intent_gate": {"exit_code": 0, "verdict": "pass"}},
    "summary": {"lanes_total": 1, "lanes_complete": 1, "files_created_total": 1},
    "delta_files": ["a.py"],
}

VALID_MRP = {
    "schema": "mrp/1.0",
    "plan_id": "plan-test-1",
    "phase": "phase2_parallel",
    "template_version": "v0.20.0",
    "intent_gate_hash": "sha256:" + "c" * 64,
    "created_at": "2026-02-25T00:00:00Z",
    "status": "planning_complete",
    "objectives": ["Build the core module"],
    "waves": [{"wave": 1, "name": "Planning", "status": "complete"}],
    "lanes": {
        "lane_a": {
            "name": "Lane A",
            "description": "Core work",
            "file_ownership": ["src/core.py"],
            "budget": {"tokens_estimated": 5000, "tokens_max": 10000},
        }
    },
    "budgets": {
        "total_estimated": 5000,
        "total_max": 10000,
        "per_lane": {"lane_a": {"estimated": 5000, "max": 10000}},
    },
    "integration_checkpoints": [
        {
            "id": "ic-1",
            "name": "Intent gate",
            "gate_command": "script/intent-gate",
            "pass_criteria": "exit 0",
        }
    ],
    "risks": [
        {"id": "R-001", "severity": "low", "description": "Minor risk", "mitigation": "Monitor"}
    ],
    "success_criteria": [{"id": "SC-01", "criterion": "All tests pass", "mandatory": True}],
}

VALID_EVIDENCE = {
    "schema_version": "evidence/1.0",
    "change_id": "PR-42",
    "tier": 1,
    "summary": "Added authentication module",
    "verification": {"commands_run": ["pytest"], "result": "pass"},
}

VALID_ROLE_ASSIGNMENTS = {
    "version": "1.0",
    "project": {"name": "test-project"},
    "roles": {
        "orchestrator": {
            "provider": "anthropic", "model": "claude-opus-4-6", "tool": "claude-code"
        },
        "builder": {
            "provider": "anthropic", "model": "claude-sonnet-4-6", "tool": "claude-code"
        },
    },
}

VALID_A2A_HANDOFF = {
    "schema_version": "a2a-lite/1.0",
    "from_role": "builder",
    "to_role": "validator",
    "goal": "Validate the authentication module",
    "touched_files": ["src/auth.py"],
    "verification_status": {"commands_run": ["pytest"], "result": "pass"},
}

VALID_DLIF_ENVELOPE = {
    "schema_version": "dlif/1.0",
    "run_id": "550e8400-e29b-41d4-a716-446655440000",
    "timestamp_utc": "2026-02-25T10:00:00Z",
    "caller": {"tool": "claude-code"},
    "model": {"provider": "anthropic", "model_id": "claude-opus-4-6"},
    "parameters": {"temperature": 0, "max_tokens": 4096},
    "messages": [{"role": "user", "content": "Hello"}],
    "policy": {"tier": 1, "cache_mode": "off", "evidence_mode": "on"},
}

VALID_GENAI_TELEMETRY = {
    "schema_version": "genai-telemetry/1.0",
    "event_type": "llm.request",
    "timestamp_utc": "2026-02-25T10:00:00Z",
}

VALID_AICS_MANIFEST = {
    "aics_version": "1.0.0",
    "project": {
        "name": "Test", "type": "repository-template",
        "owners": ["@dev"], "default_mode": "exploration",
    },
    "intent": {
        "mission": "Provide a canonical template for AI governance.",
        "principles": ["Offline-first operation"],
        "non_goals": ["Cloud-only operation"],
    },
    "invariants": [
        {
            "id": "AICS-INV-001",
            "severity": "blocker",
            "enforced_in": "script/intent-gate",
            "checks": ["schema_validate"],
        }
    ],
    "policies": {
        "required_files": ["README.md"],
        "forbidden_globs": [{"pattern": "*.pem", "reason": "No private keys"}],
        "link_check": {"enabled": True, "scope": ["docs/"], "exclude": []},
    },
    "budgets": {
        "tokens": {"exploration": 50000, "production": 10000},
        "time_seconds": {"exploration": 120, "production": 60},
    },
    "evidence": {
        "receipt_dir": "intent/receipts",
        "hash_algorithm": "sha256",
        "required_artifacts": ["intent_receipt.json"],
    },
}

VALID_AICS_RECEIPT = {
    "aics_version": "1.0.0",
    "mode": "exploration",
    "run_id": "abcd1234",
    "timestamp": "2026-02-25T10:00:00Z",
    "manifest_hash": "sha256:" + "d" * 64,
    "checks": [{"name": "schema_validate", "status": "pass"}],
    "summary": {"total": 1, "pass": 1, "fail": 0, "warn": 0, "skip": 0, "blocker": 0},
    "verdict": "pass",
}

VALID_POLICY_REGISTRY = {
    "schema_version": "policy-registry/1.0",
    "policies": [
        {
            "policy_id": "INTENT-001",
            "name": "Schema validation",
            "owner": "script/intent-gate",
            "severity_default": "blocker",
            "mode": "static",
            "gate_binding": "intent-gate",
            "profile": ["core"],
            "lifecycle": {"introduced_at": "v0.15.0"},
        }
    ],
}

# ── Valid fixtures for new v1 schemas ──────────────────────────────────────

VALID_PLAN_GRAPH = {
    "schema_version": "plan-graph/1.0",
    "run_id": "abcd1234",
    "nodes": [
        {"node_id": "n1", "name": "validate", "status": "completed"},
        {"node_id": "n2", "name": "build", "depends_on": ["n1"], "status": "pending"},
    ],
}

VALID_EVENT_ENVELOPE = {
    "schema_version": "event-envelope/1.0",
    "event_id": "evt-001",
    "event_type": "policy_decision",
    "ts": "2026-02-25T10:00:00Z",
    "run_id": "abcd1234",
}

VALID_ARTIFACT_MANIFEST = {
    "schema_version": "artifact-manifest/1.0",
    "run_id": "abcd1234",
    "artifacts": [
        {"digest": "sha256:" + "e" * 64, "name": "output.json", "size_bytes": 1024}
    ],
}

VALID_POLICY_DECISION = {
    "schema_version": "policy-decision/1.0",
    "run_id": "abcd1234",
    "policy_id": "INTENT-001",
    "decision": "allow",
    "decided_at": "2026-02-25T10:00:00Z",
}

VALID_HIL_CHECKPOINT = {
    "schema_version": "hil-checkpoint/1.0",
    "run_id": "abcd1234",
    "checkpoint_id": "cp-1",
    "prompt": "Approve deployment to production?",
    "status": "approved",
    "requested_at": "2026-02-25T10:00:00Z",
}

VALID_EVIDENCE_PACK_MANIFEST = {
    "schema_version": "evidence-pack-manifest/1.0",
    "run_id": "abcd1234",
    "created_at": "2026-02-25T10:00:00Z",
    "canonical_fields": ["/components", "/hashes", "/summary/verdict"],
    "metadata_fields": ["/created_at", "/run_id"],
    "components": {"run_manifest": "manifest.json"},
    "hashes": {"manifest.json": "sha256:" + "f" * 64},
}

VALID_GATE_RECEIPT = {
    "schema_version": "gate-receipt/1.0",
    "receipt_id": "aabb0011",
    "gate_name": "intent-gate",
    "run_id": "abcd1234",
    "task_id": "T-0201",
    "decision": "pass",
    "timestamp": "2026-02-25T10:05:00Z",
    "evidence_hash": "sha256:" + "e" * 64,
    "previous_receipt_hash": None,
}

VALID_EVAL_SUITE = {
    "schema_version": "eval-suite/1.0",
    "suite_id": "test-suite-1",
    "cases": [
        {"case_id": "tc-1", "input": {"x": 1}, "expected": {"y": 2}},
        {"case_id": "tc-2", "input": {"x": 2}, "expected": {"y": 4}, "comparison": "exact"},
    ],
}

VALID_EVAL_RESULT = {
    "schema_version": "eval-result/1.0",
    "suite_id": "test-suite-1",
    "run_id": "abcd1234",
    "evaluated_at": "2026-02-25T10:00:00Z",
    "results": [
        {"case_id": "tc-1", "status": "pass"},
        {"case_id": "tc-2", "status": "fail", "message": "Mismatch"},
    ],
    "summary": {"total": 2, "passed": 1, "failed": 1},
}

VALID_REGRESSION_GATE = {
    "schema_version": "regression-gate/1.0",
    "gate_id": "rg-1",
    "suite_id": "test-suite-1",
    "golden_ref": "goldens/test-suite-1.json",
    "thresholds": {"min_pass_rate": 0.9},
}

VALID_GOLDEN_ARTIFACT = {
    "schema_version": "golden-artifact/1.0",
    "artifact_id": "ga-1",
    "suite_id": "test-suite-1",
    "digest": "sha256:" + "0" * 64,
    "content": {"expected": "output"},
}

# ── Tier-2 fixtures ─────────────────────────────────────────────────────────

VALID_TIER2_EVENT_ENVELOPE = {
    "schema_version": "tier2-event-envelope/1.0",
    "event_id": "evt-001",
    "event_type": "RUN_STARTED",
    "timestamp": "2025-01-01T00:00:00Z",
    "run_id": "e2e00001",
    "payload": {"message": "Run started"},
    "metadata": {},
}

VALID_TIER2_PACK_MANIFEST = {
    "schema_version": "tier2-pack-manifest/1.0",
    "run_id": "e2e00001",
    "created_at": "2025-01-01T00:00:00Z",
    "components": {
        "plan_graph": "plan_graph.json",
        "events": "events.ndjson",
    },
    "summary": {"total_files": 2, "total_artifacts": 0},
}

VALID_TIER2_POLICY_DECISION = {
    "schema_version": "tier2-policy-decision/1.0",
    "run_id": "e2e00001",
    "policy_id": "default",
    "decision": "allow",
    "decided_at": "2025-01-01T00:00:00Z",
    "reason": "Tool 'read_file' is permitted",
    "context": {"action": "tool_call", "resource": "read_file", "reason_code": "TOOL_ALLOWED"},
}

VALID_TIER2_RUNTIME_CONTEXT = {
    "schema_version": "tier2-runtime-context/1.0",
    "python_version": "3.12.0",
    "platform": "Linux",
    "platform_version": "#1 SMP",
    "architecture": "x86_64",
    "repo_version": "0.1.0",
    "workspace_path": "<WORKSPACE>",
    "dependencies": [
        {"name": "jsonschema", "version": "4.23.0"},
    ],
}

VALID_TIER2_SPAN_ENVELOPE = {
    "schema_version": "tier2-span-envelope/1.0",
    "span_id": "span-001",
    "trace_id": "trace-001",
    "name": "orchestration_run",
    "start_time": "2025-01-01T00:00:00Z",
    "end_time": "2025-01-01T00:00:12Z",
    "status": "ok",
    "parent_span_id": "",
    "attributes": {"run_id": "e2e00001"},
    "events": [],
}

VALID_TIER2_SUMMARY = {
    "schema_version": "tier2-summary/1.0",
    "total_events": 13,
    "events_by_type": {"RUN_STARTED": 1, "TOOL_INVOKED": 2, "RUN_COMPLETED": 1},
    "events_by_category": {"system": 2, "tool": 2},
    "total_spans": 5,
    "spans_by_status": {"ok": 5},
}

VALID_AGENT_PROFILE = {
    "schema_version": "agent-profile/1.0",
    "agent_id": "claude_code",
    "agent_version": "claude-sonnet-4-5-20250929",
    "profile_version": "1.0.0",
    "confidence_by_task": {
        "code_generation": 0.90,
        "documentation": 0.95,
        "ui_ux": 0.60,
        "schema_design": 0.85,
    },
    "failure_patterns": ["scope_expansion", "hallucinated_paths", "overconfident_edits"],
    "evidence_sources": [
        {
            "source_type": "langfuse_trace",
            "source_id": "trace-abc123",
            "measured_at": "2026-02-25T10:00:00Z",
        },
        {
            "source_type": "eval_result",
            "source_id": "eval-xyz789",
            "measured_at": "2026-02-25T11:00:00Z",
        },
    ],
    "updated_at": "2026-02-25T12:00:00Z",
}

VALID_CAPABILITY_BOUNDARY_REGISTRY = {
    "schema_version": "capability-boundary-registry/1.0",
    "registry_version": "1.0.0",
    "boundaries": [
        {
            "task_category": "code_generation",
            "agent_id": None,
            "risk_tier": "low",
            "action": "autonomous",
            "conditions": None,
        },
        {
            "task_category": "deployment",
            "agent_id": "claude_code",
            "risk_tier": "high",
            "action": "requires_hil",
            "conditions": "requires approval from team lead",
        },
        {
            "task_category": "database_migration",
            "agent_id": None,
            "risk_tier": "critical",
            "action": "forbidden",
            "conditions": "must be executed by DBA only",
        },
    ],
    "updated_at": "2026-02-25T12:00:00Z",
}

VALID_FAILURE_TAXONOMY = {
    "taxonomy_version": "1.0.0",
    "failure_types": [
        {
            "failure_id": "FT-001",
            "name": "scope_expansion",
            "category": "scope",
            "severity": "high",
            "description": (
                "Agent expands task scope beyond original requirements, "
                "adding features or changes not requested."
            ),
            "detection_probes": [
                "git diff --stat vs. original issue acceptance criteria",
                "file count variance > 20%",
                "LOC variance > 30%",
            ],
            "mitigation": (
                "Break down tasks into smaller, atomic units. "
                "Use explicit scope constraints in prompts."
            ),
        },
        {
            "failure_id": "FT-002",
            "name": "hallucinated_paths",
            "category": "correctness",
            "severity": "critical",
            "description": (
                "Agent references or attempts to modify files "
                "that do not exist in the codebase."
            ),
            "detection_probes": [
                "grep for file references in tool calls vs. actual filesystem",
                "count of 'file not found' errors",
            ],
            "mitigation": (
                "Always validate file existence before operations. "
                "Use file search tools to verify paths."
            ),
        },
        {
            "failure_id": "FT-003",
            "name": "schema_drift",
            "category": "governance",
            "severity": "medium",
            "description": (
                "Agent modifies schemas without updating lock files "
                "or dependent systems."
            ),
            "detection_probes": [
                "schema file modification without contracts.lock.json update",
                "missing test fixtures for new schemas",
            ],
            "mitigation": (
                "Run abi-core-lock after schema changes. "
                "Enforce lock file validation in CI."
            ),
        },
    ],
    "updated_at": "2026-03-05T10:00:00Z",
}

VALID_GOVERNANCE_MANIFEST = {
    "manifest_version": "1.0.0",
    "repo_name": "abi-control-core",
    "required_gates": ["lint", "test", "schema_validate", "governance_verify"],
    "required_schemas": [
        "run_manifest.schema.json",
        "evidence_pack_manifest.schema.json",
    ],
    "policy_profile": "default",
    "hil_overrides": {
        "deployment": "high",
        "schema_changes": "medium",
    },
    "evidence_requirements": [
        "run_manifest",
        "gate_receipt",
        "evidence_pack_manifest",
    ],
    "updated_at": "2026-03-05T10:00:00Z",
}

VALID_SKILL_MANIFEST = {
    "schema_version": "skill-manifest/1.0",
    "skill_id": "schema_validation",
    "version": "1.0.0",
    "description": "Validates JSON documents against ABI contract schemas",
    "inputs_schema": {
        "type": "object",
        "properties": {
            "document": {"type": "object"},
            "schema_name": {"type": "string"},
        },
        "required": ["document", "schema_name"],
    },
    "outputs_schema": {
        "type": "object",
        "properties": {
            "valid": {"type": "boolean"},
            "errors": {"type": "array"},
        },
        "required": ["valid"],
    },
    "compatible_providers": ["anthropic", "openai"],
    "min_context_window": 8192,
    "requires_tool_calling": True,
    "evidence_requirements": ["validation_receipt"],
    "deterministic": True,
}

VALID_SEAM_EVENT = {
    "schema_version": "seam-event/1.0",
    "event_id": "seam-001",
    "event_type": "plan_published",
    "run_id": "abcd1234",
    "phase": "planning",
    "from_actor": "claude-code",
    "to_actor": "human",
    "payload": {
        "summary": "Plan has been published and is ready for review",
        "plan_id": "plan-001",
    },
    "timestamp": "2026-03-05T10:00:00Z",
}

VALID_HIL_POLICY = {
    "policy_version": "1.0",
    "default_hil_level": "advisory",
    "rules": [
        {
            "when": {
                "phase": "code_generation",
                "agent": "claude_code",
                "task_category": None,
            },
            "require": {
                "human_review": "advisory",
                "auto_approve_if": None,
            },
        },
        {
            "when": {
                "phase": "deployment",
                "agent": None,
                "task_category": None,
            },
            "require": {
                "human_review": "mandatory",
                "auto_approve_if": None,
            },
        },
        {
            "when": {
                "phase": "documentation",
                "agent": "claude_code",
                "task_category": None,
            },
            "require": {
                "human_review": "none",
                "auto_approve_if": None,
            },
        },
    ],
    "escalation": {
        "on_failure_count": 3,
        "escalate_to": "mandatory",
    },
}

VALID_CALIBRATION_METRICS = {
    "schema_version": "calibration-metrics/1.0",
    "metrics_version": "1.0.0",
    "agent_id": "claude_code",
    "period": {
        "start": "2026-03-01T00:00:00Z",
        "end": "2026-03-05T23:59:59Z",
    },
    "task_metrics": [
        {
            "task_category": "code_generation",
            "attempts": 100,
            "successes": 85,
            "failures": 15,
            "success_rate": 0.85,
            "avg_tokens": 2500.5,
            "avg_latency_ms": 1250.0,
            "failure_types": {
                "FT-001": 10,
                "FT-002": 5,
            },
        },
        {
            "task_category": "documentation",
            "attempts": 50,
            "successes": 48,
            "failures": 2,
            "success_rate": 0.96,
            "avg_tokens": 1200.0,
            "avg_latency_ms": 800.0,
            "failure_types": {
                "FT-001": 2,
            },
        },
        {
            "task_category": "schema_design",
            "attempts": 20,
            "successes": 18,
            "failures": 2,
            "success_rate": 0.90,
        },
    ],
    "human_correction_rate": 0.12,
    "computed_at": "2026-03-05T10:00:00Z",
}

VALID_GATES_LEDGER = {
    "schema_version": "gates-ledger/1.0",
    "ledger_version": "1.0.0",
    "run_id": "abcd1234",
    "entries": [
        {
            "sequence_number": 1,
            "gate_receipt": {
                "schema_version": "gate-receipt/1.0",
                "receipt_id": "aabb0011",
                "gate_name": "intent-gate",
                "run_id": "abcd1234",
                "task_id": "T-0201",
                "decision": "pass",
                "timestamp": "2026-03-05T10:00:00Z",
                "evidence_hash": "sha256:" + "e" * 64,
                "previous_receipt_hash": None,
            },
        },
        {
            "sequence_number": 2,
            "gate_receipt": {
                "schema_version": "gate-receipt/1.0",
                "receipt_id": "aabb0022",
                "gate_name": "context-gate",
                "run_id": "abcd1234",
                "task_id": "T-0201",
                "decision": "pass",
                "timestamp": "2026-03-05T10:01:00Z",
                "evidence_hash": "sha256:" + "f" * 64,
                "previous_receipt_hash": "sha256:" + "d" * 64,
            },
        },
    ],
    "integrity_hash": "sha256:" + "a" * 64,
}

VALID_GATE_LEDGER = {
    "schema_version": "gate-ledger/1.0",
    "ledger_id": "550e8400-e29b-41d4-a716-446655440000",
    "run_id": "run123",
    "repo": "abi-control-core",
    "repo_head_sha": "a" * 40,
    "gates": [
        {
            "gate_id": "lint",
            "gate_version": "1.0.0",
            "sequence": 0,
            "status": "passed",
            "inputs_hash": "sha256:" + "a" * 64,
            "outputs_hash": "sha256:" + "b" * 64,
            "receipt_hash": "sha256:2169918751a6c6ab81f557de86736b7ef66381ff04748965dfd07f593c8fb601",
            "previous_receipt_hash": None,
            "started_at": "2024-01-01T00:00:00Z",
            "completed_at": "2024-01-01T00:01:00Z",
        },
        {
            "gate_id": "test",
            "gate_version": "1.0.0",
            "sequence": 1,
            "status": "passed",
            "inputs_hash": "sha256:" + "c" * 64,
            "outputs_hash": "sha256:" + "d" * 64,
            "receipt_hash": "sha256:0110f293628ba9bb6fde7480b6017b7c8d65363f949a62de6048f27a19067491",
            "previous_receipt_hash": "sha256:2169918751a6c6ab81f557de86736b7ef66381ff04748965dfd07f593c8fb601",
            "started_at": "2024-01-01T00:01:00Z",
            "completed_at": "2024-01-01T00:02:00Z",
        },
    ],
    "final_governance_hash": "sha256:691aa26e8dbb2527d65b953215e2d92171260cf0ff3323a4b28b952490730c39",
    "created_at": "2024-01-01T00:02:00Z",
}

VALID_CALIBRATION_REPORT = {
    "report_id": "550e8400-e29b-41d4-a716-446655440000",
    "run_id": "run123",
    "project": "StatTracker",
    "generated_at": "2026-03-05T10:00:00Z",
    "summary": {
        "total_tasks": 100,
        "passed": 85,
        "failed": 10,
        "pass_unverified": 3,
        "skipped": 2,
        "success_rate": 0.85,
        "avg_iterations": 1.5,
        "total_duration_seconds": 3600.5,
    },
    "agent_breakdown": [
        {
            "agent": "claude_code",
            "tasks_assigned": 60,
            "tasks_passed": 55,
            "tasks_failed": 5,
            "avg_iterations": 1.3,
            "failure_classes": {"hallucinated_path": 3, "schema_drift": 2},
        },
        {
            "agent": "gpt-4.1",
            "tasks_assigned": 40,
            "tasks_passed": 30,
            "tasks_failed": 5,
            "avg_iterations": 1.8,
            "failure_classes": {"scope_expansion": 2, "import_chain_break": 3},
        },
    ],
    "cost_breakdown": {
        "total_tokens": 500000,
        "total_cost_usd": 25.50,
        "cost_per_task": 0.255,
        "cost_per_passed_task": 0.30,
        "builder_tokens": 400000,
        "verifier_tokens": 100000,
    },
    "failure_distribution": {
        "hallucinated_path": 3,
        "schema_drift": 2,
        "scope_expansion": 2,
        "import_chain_break": 3,
    },
    "hil_metrics": {
        "tasks_requiring_hil": 20,
        "tasks_auto_approved": 80,
        "tasks_human_approved": 15,
        "hil_rate": 0.20,
    },
    "routing_performance": [
        {
            "task_category": "code_generation",
            "agent": "claude_code",
            "success_rate": 0.90,
            "avg_iterations": 1.2,
        },
        {
            "task_category": "documentation",
            "agent": "gpt-4.1",
            "success_rate": 0.75,
            "avg_iterations": 1.6,
        },
    ],
    "recommendations": [
        "High retry rate suggests prompts may need refinement",
    ],
}

VALID_REFACTORING_ANALYSIS = {
    "analysis_version": "1.0.0",
    "repository": {
        "name": "my-awesome-project",
        "language_breakdown": {"Python": 60, "JavaScript": 30, "YAML": 10},
        "framework": "FastAPI",
        "test_coverage": 75.5,
        "total_files": 250,
        "total_loc": 15000,
    },
    "overall_health_score": 72,
    "categories": {
        "works_well": [
            {
                "item": "Test suite coverage",
                "details": "Well-structured test suite with good coverage across core modules.",
                "recommendation": "Maintain current testing practices and expand to new features.",
            }
        ],
        "needs_improvement": [
            {
                "item": "Error handling consistency",
                "effort": "medium",
                "details": "Inconsistent error handling patterns across API endpoints.",
                "recommendation": (
                    "Standardize error handling using middleware and custom exception classes."
                ),
                "affected_files": 45,
                "estimated_tokens": 12000,
            }
        ],
        "needs_rewrite": [
            {
                "item": "Legacy authentication module",
                "effort": "high",
                "details": (
                    "Authentication code predates current security standards "
                    "and uses deprecated libraries."
                ),
                "recommendation": "Migrate to modern OAuth2 implementation with JWT tokens.",
                "affected_files": 12,
                "estimated_tokens": 25000,
                "risk": "high - core security functionality",
            }
        ],
        "quick_wins": [
            {
                "item": "Remove unused imports",
                "effort": "low",
                "auto_fixable": True,
                "affected_files": 89,
            }
        ],
    },
    "analyzed_at": "2026-03-05T10:00:00Z",
}

VALID_BUDGET_STATE = {
    "schema_version": "budget-state/1.0",
    "budget_id": "daily-budget-2026-03-05",
    "ru_allocated": 10000,
    "ru_consumed": 4500,
    "ru_remaining": 5500,
    "zone": "YELLOW",
    "thresholds": {
        "yellow_pct": 60,
        "red_pct": 80,
        "exhausted_pct": 95,
    },
    "concurrency": {
        "active_slots": 3,
        "max_slots": 10,
    },
    "window": {
        "period": "daily",
        "window_start": "2026-03-05T00:00:00Z",
        "window_end": "2026-03-06T00:00:00Z",
    },
    "recorded_at": "2026-03-05T10:30:00Z",
}

VALID_MODEL_REGISTRY = {
    "schema_version": "model-registry/1.0",
    "providers": [
        {
            "provider_id": "anthropic",
            "display_name": "Anthropic",
            "channels": [
                {
                    "channel_id": "api-main",
                    "channel_type": "api",
                    "models": [
                        {
                            "model_id": "claude-sonnet-4-5",
                            "display_name": "Claude Sonnet 4.5",
                            "capabilities": ["code_generation", "reasoning", "tool_use"],
                            "tier": "frontier",
                            "context_window": 200000,
                            "max_output_tokens": 8192,
                            "ru_per_1k_input": 3.0,
                            "ru_per_1k_output": 15.0,
                        }
                    ],
                    "rate_limit_rpm": 50,
                }
            ],
        }
    ],
}

VALID_PROJECT_PROFILE = {
    "schema_version": "project-profile/1.0",
    "profile_id": "secure-project-alpha",
    "description": "High-security project requiring local-only models",
    "constraints": {
        "no_external_models": True,
        "disallow_providers": ["openai"],
        "require_frontier": False,
        "max_concurrency": 5,
        "max_ru_per_day": 50000,
    },
    "preferred_models": ["ollama/local/llama3"],
    "fallback_chain_override": "local-only-chain",
    "shadow_eval": {
        "enabled": False,
    },
}

VALID_ROUTING_DECISION_RECORD = {
    "schema_version": "routing-decision-record/1.0",
    "decision_id": "dec-20260305-103045-abc123",
    "run_id": "run-test-001",
    "decided_at": "2026-03-05T10:30:45Z",
    "task": {
        "task_type": "code_generation",
        "capabilities_required": ["code_generation", "tool_use"],
        "tier_required": "frontier",
        "estimated_input_tokens": 2000,
        "estimated_output_tokens": 1000,
    },
    "candidate_set": [
        {
            "model_ref": "anthropic/api-main/claude-sonnet-4-5",
            "eligible": True,
        },
        {
            "model_ref": "openai/api/gpt-4",
            "eligible": False,
            "rejection_reason": "provider_disallowed",
        },
    ],
    "selected": {
        "model_ref": "anthropic/api-main/claude-sonnet-4-5",
        "channel_id": "api-main",
        "provider_id": "anthropic",
        "tier": "frontier",
        "ru_estimate": 21.0,
    },
    "matched_rule_id": "rule-frontier-code",
    "constraints_applied": ["constraint-tier-frontier"],
    "quota_snapshot": {
        "zone": "YELLOW",
        "ru_consumed": 4500,
        "ru_remaining": 5500,
        "ru_allocated": 10000,
        "active_concurrency": 3,
        "concurrency_limit": 10,
    },
    "fallback_used": False,
    "no_route": False,
    "record_hash": "a1b2c3d4e5f6789012345678901234567890123456789012345678901234abcd",
}

VALID_ROUTING_POLICY = {
    "schema_version": "routing-policy/1.0",
    "policy_id": "default-routing-policy",
    "description": "Default routing policy for standard workloads",
    "rules": [
        {
            "rule_id": "rule-frontier-code",
            "priority": 10,
            "match": {
                "task_type": "code_generation",
                "tier_required": "frontier",
                "capabilities_required": ["code_generation", "tool_use"],
            },
            "route_to": {
                "model_ref": "anthropic/api-main/claude-sonnet-4-5",
                "channel_preference": ["api-main", "subscription"],
                "fallback_chain": "standard-fallback",
            },
        }
    ],
    "fallback_chains": {
        "standard-fallback": [
            "anthropic/subscription/claude-sonnet-4",
            "ollama/local/llama3",
        ],
        "local-only-chain": [
            "ollama/local/llama3",
        ],
    },
}

# ── Parametrized tests ─────────────────────────────────────────────────────

ALL_VALID_FIXTURES = [
    ("run_manifest.schema.json", VALID_RUN_MANIFEST),
    ("budget_receipt.schema.json", VALID_BUDGET_RECEIPT),
    ("span.schema.json", VALID_SPAN),
    ("trace_event.schema.json", VALID_TRACE_EVENT),
    ("context_manifest.schema.json", VALID_CONTEXT_MANIFEST),
    ("crp.schema.json", VALID_CRP),
    ("mrp.schema.json", VALID_MRP),
    ("evidence.schema.json", VALID_EVIDENCE),
    ("role_assignments.schema.json", VALID_ROLE_ASSIGNMENTS),
    ("a2a_handoff.schema.json", VALID_A2A_HANDOFF),
    ("dlif_envelope.schema.json", VALID_DLIF_ENVELOPE),
    ("genai_telemetry.schema.json", VALID_GENAI_TELEMETRY),
    ("aics_manifest.schema.json", VALID_AICS_MANIFEST),
    ("aics_receipt.schema.json", VALID_AICS_RECEIPT),
    ("policy_registry.schema.json", VALID_POLICY_REGISTRY),
    ("plan_graph.schema.json", VALID_PLAN_GRAPH),
    ("event_envelope.schema.json", VALID_EVENT_ENVELOPE),
    ("artifact_manifest.schema.json", VALID_ARTIFACT_MANIFEST),
    ("policy_decision.schema.json", VALID_POLICY_DECISION),
    ("hil_checkpoint.schema.json", VALID_HIL_CHECKPOINT),
    ("evidence_pack_manifest.schema.json", VALID_EVIDENCE_PACK_MANIFEST),
    ("gate_receipt.schema.json", VALID_GATE_RECEIPT),
    ("eval_suite.schema.json", VALID_EVAL_SUITE),
    ("eval_result.schema.json", VALID_EVAL_RESULT),
    ("regression_gate.schema.json", VALID_REGRESSION_GATE),
    ("golden_artifact.schema.json", VALID_GOLDEN_ARTIFACT),
    # Tier-2 schemas
    ("tier2_event_envelope.schema.json", VALID_TIER2_EVENT_ENVELOPE),
    ("tier2_pack_manifest.schema.json", VALID_TIER2_PACK_MANIFEST),
    ("tier2_policy_decision.schema.json", VALID_TIER2_POLICY_DECISION),
    ("tier2_runtime_context.schema.json", VALID_TIER2_RUNTIME_CONTEXT),
    ("tier2_span_envelope.schema.json", VALID_TIER2_SPAN_ENVELOPE),
    ("tier2_summary.schema.json", VALID_TIER2_SUMMARY),
    ("agent_profile.schema.json", VALID_AGENT_PROFILE),
    ("capability_boundary_registry.schema.json", VALID_CAPABILITY_BOUNDARY_REGISTRY),
    ("failure_taxonomy.schema.json", VALID_FAILURE_TAXONOMY),
    ("governance_manifest.schema.json", VALID_GOVERNANCE_MANIFEST),
    ("skill_manifest.schema.json", VALID_SKILL_MANIFEST),
    ("seam_event.schema.json", VALID_SEAM_EVENT),
    ("hil_policy.schema.json", VALID_HIL_POLICY),
    ("calibration_metrics.schema.json", VALID_CALIBRATION_METRICS),
    ("gates_ledger.schema.json", VALID_GATES_LEDGER),
    ("gate_ledger.schema.json", VALID_GATE_LEDGER),
    ("refactoring_analysis.schema.json", VALID_REFACTORING_ANALYSIS),
    # Wave 3 routing schemas
    ("budget_state.schema.json", VALID_BUDGET_STATE),
    ("model_registry.schema.json", VALID_MODEL_REGISTRY),
    ("project_profile.schema.json", VALID_PROJECT_PROFILE),
    ("routing_decision_record.schema.json", VALID_ROUTING_DECISION_RECORD),
    ("routing_policy.schema.json", VALID_ROUTING_POLICY),
]


@pytest.mark.parametrize(
    "schema_file,instance",
    ALL_VALID_FIXTURES,
    ids=[f[0].replace(".schema.json", "") for f in ALL_VALID_FIXTURES],
)
def test_valid_fixture_passes(schema_file: str, instance: dict) -> None:
    """Each valid fixture must pass validation."""
    validate_json(instance, schema_file)


# ── Negative tests ─────────────────────────────────────────────────────────

def test_invalid_run_manifest_missing_field() -> None:
    """run_manifest without required 'verdict' must fail."""
    invalid = {**VALID_RUN_MANIFEST}
    del invalid["verdict"]
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "run_manifest.schema.json")
    assert any("verdict" in msg for _, msg in exc_info.value.errors)


def test_invalid_run_manifest_bad_run_id() -> None:
    """run_manifest with a non-hex run_id must fail."""
    invalid = {**VALID_RUN_MANIFEST, "run_id": "ZZZZZZZZ"}
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "run_manifest.schema.json")


def test_invalid_eval_suite_empty_cases() -> None:
    """eval_suite with empty cases array must fail."""
    invalid = {**VALID_EVAL_SUITE, "cases": []}
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "eval_suite.schema.json")


def test_schema_not_found() -> None:
    """Requesting a nonexistent schema must raise FileNotFoundError."""
    with pytest.raises(FileNotFoundError):
        validate_json({}, "nonexistent.schema.json")


def test_validation_error_has_json_pointers() -> None:
    """Validation errors must include JSON pointer paths."""
    bad_summary = {
        "total": "not_a_number", "passed": 0, "failed": 0, "skipped": 0, "blocker": 0
    }
    invalid = {**VALID_RUN_MANIFEST, "summary": bad_summary}
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "run_manifest.schema.json")
    pointers = [ptr for ptr, _ in exc_info.value.errors]
    assert any("/summary" in p for p in pointers)


# ── Tier-2 negative tests ───────────────────────────────────────────────────

def test_invalid_tier2_event_missing_event_id() -> None:
    """tier2_event_envelope without 'event_id' must fail."""
    invalid = {**VALID_TIER2_EVENT_ENVELOPE}
    del invalid["event_id"]
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "tier2_event_envelope.schema.json")
    assert any("event_id" in msg for _, msg in exc_info.value.errors)


def test_invalid_tier2_event_wrong_schema_version() -> None:
    """tier2_event_envelope with wrong schema_version const must fail."""
    invalid = {**VALID_TIER2_EVENT_ENVELOPE, "schema_version": "event-envelope/1.0"}
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "tier2_event_envelope.schema.json")


def test_invalid_tier2_policy_decision_bad_decision() -> None:
    """tier2_policy_decision with unknown decision value must fail."""
    invalid = {**VALID_TIER2_POLICY_DECISION, "decision": "require_approval"}
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "tier2_policy_decision.schema.json")


def test_invalid_tier2_span_bad_status() -> None:
    """tier2_span_envelope with unknown status must fail."""
    invalid = {**VALID_TIER2_SPAN_ENVELOPE, "status": "warn"}
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "tier2_span_envelope.schema.json")


def test_invalid_tier2_runtime_context_wrong_workspace() -> None:
    """tier2_runtime_context with un-redacted workspace_path must fail."""
    invalid = {**VALID_TIER2_RUNTIME_CONTEXT, "workspace_path": "/home/user/project"}
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "tier2_runtime_context.schema.json")


def test_invalid_tier2_pack_manifest_missing_components() -> None:
    """tier2_pack_manifest without 'components' must fail."""
    invalid = {**VALID_TIER2_PACK_MANIFEST}
    del invalid["components"]
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "tier2_pack_manifest.schema.json")
    assert any("components" in msg for _, msg in exc_info.value.errors)


def test_invalid_tier2_summary_missing_events_by_type() -> None:
    """tier2_summary without 'events_by_type' must fail."""
    invalid = {**VALID_TIER2_SUMMARY}
    del invalid["events_by_type"]
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "tier2_summary.schema.json")
    assert any("events_by_type" in msg for _, msg in exc_info.value.errors)


# ── prompt_bundle_sha256 optional field tests ──────────────────────────────

def test_run_manifest_with_prompt_bundle_sha256() -> None:
    """run_manifest validates with optional prompt_bundle_sha256."""
    valid = {**VALID_RUN_MANIFEST, "prompt_bundle_sha256": "a" * 64}
    validate_json(valid, "run_manifest.schema.json")


def test_run_manifest_rejects_bad_prompt_bundle_sha256() -> None:
    """run_manifest rejects prompt_bundle_sha256 with wrong format."""
    invalid = {**VALID_RUN_MANIFEST, "prompt_bundle_sha256": "not-a-sha"}
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "run_manifest.schema.json")


def test_tier2_pack_manifest_with_prompt_bundle_sha256() -> None:
    """tier2_pack_manifest validates with optional prompt_bundle_sha256."""
    valid = {**VALID_TIER2_PACK_MANIFEST, "prompt_bundle_sha256": "b" * 64}
    validate_json(valid, "tier2_pack_manifest.schema.json")


# ── gate_receipt negative tests ─────────────────────────────────────────────

def test_invalid_gate_receipt_missing_required_field() -> None:
    """gate_receipt without required 'decision' must fail."""
    invalid = {**VALID_GATE_RECEIPT}
    del invalid["decision"]
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "gate_receipt.schema.json")
    assert any("decision" in msg for _, msg in exc_info.value.errors)


def test_invalid_gate_receipt_bad_decision_enum() -> None:
    """gate_receipt with invalid decision value must fail."""
    invalid = {**VALID_GATE_RECEIPT, "decision": "maybe"}
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "gate_receipt.schema.json")


def test_invalid_gate_receipt_bad_receipt_id() -> None:
    """gate_receipt with non-hex receipt_id must fail."""
    invalid = {**VALID_GATE_RECEIPT, "receipt_id": "ZZZZZZZZ"}
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "gate_receipt.schema.json")


def test_invalid_gate_receipt_bad_evidence_hash() -> None:
    """gate_receipt with malformed evidence_hash must fail."""
    invalid = {**VALID_GATE_RECEIPT, "evidence_hash": "not-a-hash"}
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "gate_receipt.schema.json")


def test_valid_gate_receipt_with_previous_hash() -> None:
    """gate_receipt validates with non-null previous_receipt_hash."""
    valid = {**VALID_GATE_RECEIPT, "previous_receipt_hash": "sha256:" + "d" * 64}
    validate_json(valid, "gate_receipt.schema.json")


def test_valid_gate_receipt_with_verdict_details() -> None:
    """gate_receipt validates with optional verdict_details."""
    valid = {
        **VALID_GATE_RECEIPT,
        "verdict_details": {
            "checks_total": 5,
            "checks_passed": 5,
            "checks_failed": 0,
            "checks_skipped": 0,
            "blocker_count": 0,
        },
    }
    validate_json(valid, "gate_receipt.schema.json")


def test_valid_gate_receipt_with_canonical_metadata_fields() -> None:
    """gate_receipt validates with explicit canonical_fields and metadata_fields."""
    valid = {
        **VALID_GATE_RECEIPT,
        "canonical_fields": ["/decision", "/evidence_hash", "/gate_name"],
        "metadata_fields": ["/receipt_id", "/timestamp"],
    }
    validate_json(valid, "gate_receipt.schema.json")


# ── agent_profile negative tests ───────────────────────────────────────────

def test_invalid_agent_profile_missing_agent_id() -> None:
    """agent_profile without 'agent_id' must fail."""
    invalid = {**VALID_AGENT_PROFILE}
    del invalid["agent_id"]
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "agent_profile.schema.json")
    assert any("agent_id" in msg for _, msg in exc_info.value.errors)


def test_invalid_agent_profile_bad_confidence_score() -> None:
    """agent_profile with confidence score > 1.0 must fail."""
    invalid = {
        **VALID_AGENT_PROFILE,
        "confidence_by_task": {"code_generation": 1.5},
    }
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "agent_profile.schema.json")


def test_invalid_agent_profile_negative_confidence_score() -> None:
    """agent_profile with negative confidence score must fail."""
    invalid = {
        **VALID_AGENT_PROFILE,
        "confidence_by_task": {"code_generation": -0.1},
    }
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "agent_profile.schema.json")


def test_invalid_agent_profile_empty_confidence_by_task() -> None:
    """agent_profile with empty confidence_by_task must fail."""
    invalid = {**VALID_AGENT_PROFILE, "confidence_by_task": {}}
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "agent_profile.schema.json")


def test_invalid_agent_profile_bad_source_type() -> None:
    """agent_profile with unknown source_type must fail."""
    invalid = {
        **VALID_AGENT_PROFILE,
        "evidence_sources": [
            {
                "source_type": "unknown_source",
                "source_id": "test-123",
                "measured_at": "2026-02-25T10:00:00Z",
            }
        ],
    }
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "agent_profile.schema.json")


def test_invalid_agent_profile_missing_evidence_source_field() -> None:
    """agent_profile with evidence_source missing required field must fail."""
    invalid = {
        **VALID_AGENT_PROFILE,
        "evidence_sources": [
            {
                "source_type": "langfuse_trace",
                "source_id": "test-123",
                # missing 'measured_at'
            }
        ],
    }
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "agent_profile.schema.json")
    assert any("measured_at" in msg for _, msg in exc_info.value.errors)


def test_valid_agent_profile_with_empty_failure_patterns() -> None:
    """agent_profile validates with empty failure_patterns array."""
    valid = {**VALID_AGENT_PROFILE, "failure_patterns": []}
    validate_json(valid, "agent_profile.schema.json")


def test_valid_agent_profile_with_empty_evidence_sources() -> None:
    """agent_profile validates with empty evidence_sources array."""
    valid = {**VALID_AGENT_PROFILE, "evidence_sources": []}
    validate_json(valid, "agent_profile.schema.json")


# ── capability_boundary_registry negative tests ────────────────────────────

def test_invalid_capability_boundary_registry_missing_registry_version() -> None:
    """capability_boundary_registry without 'registry_version' must fail."""
    invalid = {**VALID_CAPABILITY_BOUNDARY_REGISTRY}
    del invalid["registry_version"]
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "capability_boundary_registry.schema.json")
    assert any("registry_version" in msg for _, msg in exc_info.value.errors)


def test_invalid_capability_boundary_registry_missing_boundaries() -> None:
    """capability_boundary_registry without 'boundaries' must fail."""
    invalid = {**VALID_CAPABILITY_BOUNDARY_REGISTRY}
    del invalid["boundaries"]
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "capability_boundary_registry.schema.json")
    assert any("boundaries" in msg for _, msg in exc_info.value.errors)


def test_invalid_capability_boundary_registry_empty_boundaries() -> None:
    """capability_boundary_registry with empty boundaries array must fail."""
    invalid = {**VALID_CAPABILITY_BOUNDARY_REGISTRY, "boundaries": []}
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "capability_boundary_registry.schema.json")


def test_invalid_capability_boundary_registry_bad_risk_tier() -> None:
    """capability_boundary_registry with unknown risk_tier must fail."""
    invalid = {
        **VALID_CAPABILITY_BOUNDARY_REGISTRY,
        "boundaries": [
            {
                "task_category": "code_generation",
                "agent_id": None,
                "risk_tier": "ultra",
                "action": "autonomous",
                "conditions": None,
            }
        ],
    }
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "capability_boundary_registry.schema.json")


def test_invalid_capability_boundary_registry_bad_action() -> None:
    """capability_boundary_registry with unknown action must fail."""
    invalid = {
        **VALID_CAPABILITY_BOUNDARY_REGISTRY,
        "boundaries": [
            {
                "task_category": "code_generation",
                "agent_id": None,
                "risk_tier": "low",
                "action": "maybe",
                "conditions": None,
            }
        ],
    }
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "capability_boundary_registry.schema.json")


def test_invalid_capability_boundary_registry_missing_task_category() -> None:
    """capability_boundary_registry boundary without task_category must fail."""
    invalid = {
        **VALID_CAPABILITY_BOUNDARY_REGISTRY,
        "boundaries": [
            {
                "agent_id": None,
                "risk_tier": "low",
                "action": "autonomous",
                "conditions": None,
            }
        ],
    }
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "capability_boundary_registry.schema.json")
    assert any("task_category" in msg for _, msg in exc_info.value.errors)


def test_valid_capability_boundary_registry_with_notes() -> None:
    """capability_boundary_registry validates with optional notes field."""
    valid = {
        **VALID_CAPABILITY_BOUNDARY_REGISTRY,
        "notes": "Updated boundary rules to require HIL for high-risk deployments",
    }
    validate_json(valid, "capability_boundary_registry.schema.json")


def test_valid_capability_boundary_registry_with_specific_agent() -> None:
    """capability_boundary_registry validates with specific agent_id."""
    valid = {
        **VALID_CAPABILITY_BOUNDARY_REGISTRY,
        "boundaries": [
            {
                "task_category": "deployment",
                "agent_id": "gpt-4",
                "risk_tier": "high",
                "action": "requires_hil",
                "conditions": "requires approval",
            }
        ],
    }
    validate_json(valid, "capability_boundary_registry.schema.json")


# ── failure_taxonomy negative tests ────────────────────────────────────────

def test_invalid_failure_taxonomy_missing_taxonomy_version() -> None:
    """failure_taxonomy without 'taxonomy_version' must fail."""
    invalid = {**VALID_FAILURE_TAXONOMY}
    del invalid["taxonomy_version"]
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "failure_taxonomy.schema.json")
    assert any("taxonomy_version" in msg for _, msg in exc_info.value.errors)


def test_invalid_failure_taxonomy_missing_failure_types() -> None:
    """failure_taxonomy without 'failure_types' must fail."""
    invalid = {**VALID_FAILURE_TAXONOMY}
    del invalid["failure_types"]
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "failure_taxonomy.schema.json")
    assert any("failure_types" in msg for _, msg in exc_info.value.errors)


def test_invalid_failure_taxonomy_empty_failure_types() -> None:
    """failure_taxonomy with empty failure_types array must fail."""
    invalid = {**VALID_FAILURE_TAXONOMY, "failure_types": []}
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "failure_taxonomy.schema.json")


def test_invalid_failure_taxonomy_bad_taxonomy_version_format() -> None:
    """failure_taxonomy with non-semver taxonomy_version must fail."""
    invalid = {**VALID_FAILURE_TAXONOMY, "taxonomy_version": "v1.0"}
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "failure_taxonomy.schema.json")


def test_invalid_failure_taxonomy_bad_failure_id_format() -> None:
    """failure_taxonomy with malformed failure_id must fail."""
    invalid = {
        **VALID_FAILURE_TAXONOMY,
        "failure_types": [
            {
                "failure_id": "FT-01",  # Wrong format, should be FT-001
                "name": "test_failure",
                "category": "correctness",
                "severity": "low",
                "description": "Test",
                "detection_probes": ["test"],
                "mitigation": "Fix it",
            }
        ],
    }
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "failure_taxonomy.schema.json")


def test_invalid_failure_taxonomy_bad_category() -> None:
    """failure_taxonomy with unknown category must fail."""
    invalid = {
        **VALID_FAILURE_TAXONOMY,
        "failure_types": [
            {
                "failure_id": "FT-001",
                "name": "test_failure",
                "category": "unknown_category",
                "severity": "low",
                "description": "Test",
                "detection_probes": ["test"],
                "mitigation": "Fix it",
            }
        ],
    }
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "failure_taxonomy.schema.json")


def test_invalid_failure_taxonomy_bad_severity() -> None:
    """failure_taxonomy with unknown severity must fail."""
    invalid = {
        **VALID_FAILURE_TAXONOMY,
        "failure_types": [
            {
                "failure_id": "FT-001",
                "name": "test_failure",
                "category": "correctness",
                "severity": "ultra",
                "description": "Test",
                "detection_probes": ["test"],
                "mitigation": "Fix it",
            }
        ],
    }
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "failure_taxonomy.schema.json")


def test_invalid_failure_taxonomy_empty_detection_probes() -> None:
    """failure_taxonomy with empty detection_probes array must fail."""
    invalid = {
        **VALID_FAILURE_TAXONOMY,
        "failure_types": [
            {
                "failure_id": "FT-001",
                "name": "test_failure",
                "category": "correctness",
                "severity": "low",
                "description": "Test",
                "detection_probes": [],
                "mitigation": "Fix it",
            }
        ],
    }
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "failure_taxonomy.schema.json")


def test_invalid_failure_taxonomy_missing_updated_at() -> None:
    """failure_taxonomy without 'updated_at' must fail."""
    invalid = {**VALID_FAILURE_TAXONOMY}
    del invalid["updated_at"]
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "failure_taxonomy.schema.json")
    assert any("updated_at" in msg for _, msg in exc_info.value.errors)


def test_invalid_failure_taxonomy_missing_required_field() -> None:
    """failure_taxonomy failure_type without required field must fail."""
    invalid = {
        **VALID_FAILURE_TAXONOMY,
        "failure_types": [
            {
                "failure_id": "FT-001",
                "name": "test_failure",
                "category": "correctness",
                "severity": "low",
                "description": "Test",
                # missing 'detection_probes'
                "mitigation": "Fix it",
            }
        ],
    }
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "failure_taxonomy.schema.json")
    assert any("detection_probes" in msg for _, msg in exc_info.value.errors)


# ── governance_manifest negative tests ─────────────────────────────────────

def test_invalid_governance_manifest_missing_manifest_version() -> None:
    """governance_manifest without 'manifest_version' must fail."""
    invalid = {**VALID_GOVERNANCE_MANIFEST}
    del invalid["manifest_version"]
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "governance_manifest.schema.json")
    assert any("manifest_version" in msg for _, msg in exc_info.value.errors)


def test_invalid_governance_manifest_missing_repo_name() -> None:
    """governance_manifest without 'repo_name' must fail."""
    invalid = {**VALID_GOVERNANCE_MANIFEST}
    del invalid["repo_name"]
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "governance_manifest.schema.json")
    assert any("repo_name" in msg for _, msg in exc_info.value.errors)


def test_invalid_governance_manifest_missing_required_gates() -> None:
    """governance_manifest without 'required_gates' must fail."""
    invalid = {**VALID_GOVERNANCE_MANIFEST}
    del invalid["required_gates"]
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "governance_manifest.schema.json")
    assert any("required_gates" in msg for _, msg in exc_info.value.errors)


def test_invalid_governance_manifest_empty_required_gates() -> None:
    """governance_manifest with empty required_gates array must fail."""
    invalid = {**VALID_GOVERNANCE_MANIFEST, "required_gates": []}
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "governance_manifest.schema.json")


def test_invalid_governance_manifest_bad_manifest_version_format() -> None:
    """governance_manifest with non-semver manifest_version must fail."""
    invalid = {**VALID_GOVERNANCE_MANIFEST, "manifest_version": "v1.0"}
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "governance_manifest.schema.json")


def test_invalid_governance_manifest_bad_policy_profile() -> None:
    """governance_manifest with unknown policy_profile must fail."""
    invalid = {**VALID_GOVERNANCE_MANIFEST, "policy_profile": "ultra_high"}
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "governance_manifest.schema.json")


def test_invalid_governance_manifest_bad_hil_override_value() -> None:
    """governance_manifest with invalid hil_overrides value must fail."""
    invalid = {
        **VALID_GOVERNANCE_MANIFEST,
        "hil_overrides": {"deployment": "ultra"},
    }
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "governance_manifest.schema.json")


def test_invalid_governance_manifest_missing_updated_at() -> None:
    """governance_manifest without 'updated_at' must fail."""
    invalid = {**VALID_GOVERNANCE_MANIFEST}
    del invalid["updated_at"]
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "governance_manifest.schema.json")
    assert any("updated_at" in msg for _, msg in exc_info.value.errors)


def test_valid_governance_manifest_minimal() -> None:
    """governance_manifest validates with only required fields."""
    valid = {
        "manifest_version": "1.0.0",
        "repo_name": "test-repo",
        "required_gates": ["test"],
        "updated_at": "2026-03-05T10:00:00Z",
    }
    validate_json(valid, "governance_manifest.schema.json")


def test_valid_governance_manifest_with_null_hil_overrides() -> None:
    """governance_manifest validates with null hil_overrides."""
    valid = {**VALID_GOVERNANCE_MANIFEST, "hil_overrides": None}
    validate_json(valid, "governance_manifest.schema.json")


def test_valid_governance_manifest_with_all_policy_profiles() -> None:
    """governance_manifest validates with each policy_profile value."""
    for profile in ["default", "hil_low", "hil_medium", "hil_high"]:
        valid = {**VALID_GOVERNANCE_MANIFEST, "policy_profile": profile}
        validate_json(valid, "governance_manifest.schema.json")


# ── skill_manifest negative tests ──────────────────────────────────────────

def test_invalid_skill_manifest_missing_skill_id() -> None:
    """skill_manifest without 'skill_id' must fail."""
    invalid = {**VALID_SKILL_MANIFEST}
    del invalid["skill_id"]
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "skill_manifest.schema.json")
    assert any("skill_id" in msg for _, msg in exc_info.value.errors)


def test_invalid_skill_manifest_bad_version_format() -> None:
    """skill_manifest with non-semver version must fail."""
    invalid = {**VALID_SKILL_MANIFEST, "version": "v1.0"}
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "skill_manifest.schema.json")


def test_invalid_skill_manifest_missing_version() -> None:
    """skill_manifest without 'version' must fail."""
    invalid = {**VALID_SKILL_MANIFEST}
    del invalid["version"]
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "skill_manifest.schema.json")
    assert any("version" in msg for _, msg in exc_info.value.errors)


def test_invalid_skill_manifest_empty_description() -> None:
    """skill_manifest with empty description must fail."""
    invalid = {**VALID_SKILL_MANIFEST, "description": ""}
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "skill_manifest.schema.json")


def test_invalid_skill_manifest_description_too_long() -> None:
    """skill_manifest with description > 1000 chars must fail."""
    invalid = {**VALID_SKILL_MANIFEST, "description": "x" * 1001}
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "skill_manifest.schema.json")


def test_invalid_skill_manifest_missing_inputs_schema() -> None:
    """skill_manifest without 'inputs_schema' must fail."""
    invalid = {**VALID_SKILL_MANIFEST}
    del invalid["inputs_schema"]
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "skill_manifest.schema.json")
    assert any("inputs_schema" in msg for _, msg in exc_info.value.errors)


def test_invalid_skill_manifest_bad_inputs_schema() -> None:
    """skill_manifest with inputs_schema missing 'type' must fail."""
    invalid = {**VALID_SKILL_MANIFEST, "inputs_schema": {"properties": {}}}
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "skill_manifest.schema.json")


def test_invalid_skill_manifest_missing_outputs_schema() -> None:
    """skill_manifest without 'outputs_schema' must fail."""
    invalid = {**VALID_SKILL_MANIFEST}
    del invalid["outputs_schema"]
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "skill_manifest.schema.json")
    assert any("outputs_schema" in msg for _, msg in exc_info.value.errors)


def test_invalid_skill_manifest_bad_outputs_schema() -> None:
    """skill_manifest with outputs_schema missing 'type' must fail."""
    invalid = {**VALID_SKILL_MANIFEST, "outputs_schema": {"properties": {}}}
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "skill_manifest.schema.json")


def test_invalid_skill_manifest_empty_compatible_providers() -> None:
    """skill_manifest with empty compatible_providers array must fail."""
    invalid = {**VALID_SKILL_MANIFEST, "compatible_providers": []}
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "skill_manifest.schema.json")


def test_invalid_skill_manifest_missing_compatible_providers() -> None:
    """skill_manifest without 'compatible_providers' must fail."""
    invalid = {**VALID_SKILL_MANIFEST}
    del invalid["compatible_providers"]
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "skill_manifest.schema.json")
    assert any("compatible_providers" in msg for _, msg in exc_info.value.errors)


def test_invalid_skill_manifest_negative_min_context_window() -> None:
    """skill_manifest with negative min_context_window must fail."""
    invalid = {**VALID_SKILL_MANIFEST, "min_context_window": -1}
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "skill_manifest.schema.json")


def test_invalid_skill_manifest_missing_requires_tool_calling() -> None:
    """skill_manifest without 'requires_tool_calling' must fail."""
    invalid = {**VALID_SKILL_MANIFEST}
    del invalid["requires_tool_calling"]
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "skill_manifest.schema.json")
    assert any("requires_tool_calling" in msg for _, msg in exc_info.value.errors)


def test_invalid_skill_manifest_missing_deterministic() -> None:
    """skill_manifest without 'deterministic' must fail."""
    invalid = {**VALID_SKILL_MANIFEST}
    del invalid["deterministic"]
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "skill_manifest.schema.json")
    assert any("deterministic" in msg for _, msg in exc_info.value.errors)


def test_valid_skill_manifest_with_optional_fields() -> None:
    """skill_manifest validates with optional tags and timestamps."""
    valid = {
        **VALID_SKILL_MANIFEST,
        "tags": ["validation", "tier1", "offline"],
        "created_at": "2026-03-05T10:00:00Z",
        "updated_at": "2026-03-05T12:00:00Z",
    }
    validate_json(valid, "skill_manifest.schema.json")


def test_valid_skill_manifest_with_empty_evidence_requirements() -> None:
    """skill_manifest validates with empty evidence_requirements array."""
    valid = {**VALID_SKILL_MANIFEST, "evidence_requirements": []}
    validate_json(valid, "skill_manifest.schema.json")


def test_valid_skill_manifest_deterministic_false() -> None:
    """skill_manifest validates with deterministic=false."""
    valid = {**VALID_SKILL_MANIFEST, "deterministic": False}
    validate_json(valid, "skill_manifest.schema.json")


def test_valid_skill_manifest_requires_tool_calling_false() -> None:
    """skill_manifest validates with requires_tool_calling=false."""
    valid = {**VALID_SKILL_MANIFEST, "requires_tool_calling": False}
    validate_json(valid, "skill_manifest.schema.json")


# ── seam_event negative tests ──────────────────────────────────────────────

def test_invalid_seam_event_missing_event_id() -> None:
    """seam_event without 'event_id' must fail."""
    invalid = {**VALID_SEAM_EVENT}
    del invalid["event_id"]
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "seam_event.schema.json")
    assert any("event_id" in msg for _, msg in exc_info.value.errors)


def test_invalid_seam_event_missing_event_type() -> None:
    """seam_event without 'event_type' must fail."""
    invalid = {**VALID_SEAM_EVENT}
    del invalid["event_type"]
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "seam_event.schema.json")
    assert any("event_type" in msg for _, msg in exc_info.value.errors)


def test_invalid_seam_event_bad_event_type() -> None:
    """seam_event with unknown event_type must fail."""
    invalid = {**VALID_SEAM_EVENT, "event_type": "unknown_event"}
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "seam_event.schema.json")


def test_invalid_seam_event_missing_run_id() -> None:
    """seam_event without 'run_id' must fail."""
    invalid = {**VALID_SEAM_EVENT}
    del invalid["run_id"]
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "seam_event.schema.json")
    assert any("run_id" in msg for _, msg in exc_info.value.errors)


def test_invalid_seam_event_bad_run_id() -> None:
    """seam_event with non-hex run_id must fail."""
    invalid = {**VALID_SEAM_EVENT, "run_id": "ZZZZZZZZ"}
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "seam_event.schema.json")


def test_invalid_seam_event_missing_phase() -> None:
    """seam_event without 'phase' must fail."""
    invalid = {**VALID_SEAM_EVENT}
    del invalid["phase"]
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "seam_event.schema.json")
    assert any("phase" in msg for _, msg in exc_info.value.errors)


def test_invalid_seam_event_empty_phase() -> None:
    """seam_event with empty phase must fail."""
    invalid = {**VALID_SEAM_EVENT, "phase": ""}
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "seam_event.schema.json")


def test_invalid_seam_event_missing_timestamp() -> None:
    """seam_event without 'timestamp' must fail."""
    invalid = {**VALID_SEAM_EVENT}
    del invalid["timestamp"]
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "seam_event.schema.json")
    assert any("timestamp" in msg for _, msg in exc_info.value.errors)


def test_valid_seam_event_all_event_types() -> None:
    """seam_event validates with all event_type values."""
    event_types = [
        "plan_published",
        "diff_proposed",
        "verification_complete",
        "human_signoff",
        "handoff_to_agent",
        "handoff_to_human",
    ]
    for event_type in event_types:
        valid = {**VALID_SEAM_EVENT, "event_type": event_type}
        validate_json(valid, "seam_event.schema.json")


def test_valid_seam_event_minimal() -> None:
    """seam_event validates with only required fields."""
    valid = {
        "schema_version": "seam-event/1.0",
        "event_id": "seam-001",
        "event_type": "handoff_to_human",
        "run_id": "abcd1234",
        "phase": "execution",
        "timestamp": "2026-03-05T10:00:00Z",
    }
    validate_json(valid, "seam_event.schema.json")


def test_valid_seam_event_with_complex_payload() -> None:
    """seam_event validates with complex payload object."""
    valid = {
        **VALID_SEAM_EVENT,
        "payload": {
            "summary": "Verification complete",
            "acceptance_criteria": ["all tests pass", "no linting errors"],
            "rollback_instructions": "git reset --hard HEAD~1",
            "metadata": {"duration_ms": 1234, "checks_run": 5},
        },
    }
    validate_json(valid, "seam_event.schema.json")


# ── hil_policy negative tests ──────────────────────────────────────────────

def test_invalid_hil_policy_missing_policy_version() -> None:
    """hil_policy without 'policy_version' must fail."""
    invalid = {**VALID_HIL_POLICY}
    del invalid["policy_version"]
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "hil_policy.schema.json")
    assert any("policy_version" in msg for _, msg in exc_info.value.errors)


def test_invalid_hil_policy_missing_default_hil_level() -> None:
    """hil_policy without 'default_hil_level' must fail."""
    invalid = {**VALID_HIL_POLICY}
    del invalid["default_hil_level"]
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "hil_policy.schema.json")
    assert any("default_hil_level" in msg for _, msg in exc_info.value.errors)


def test_invalid_hil_policy_missing_rules() -> None:
    """hil_policy without 'rules' must fail."""
    invalid = {**VALID_HIL_POLICY}
    del invalid["rules"]
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "hil_policy.schema.json")
    assert any("rules" in msg for _, msg in exc_info.value.errors)


def test_invalid_hil_policy_empty_rules() -> None:
    """hil_policy with empty rules array must fail."""
    invalid = {**VALID_HIL_POLICY, "rules": []}
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "hil_policy.schema.json")


def test_invalid_hil_policy_bad_default_hil_level() -> None:
    """hil_policy with unknown default_hil_level must fail."""
    invalid = {**VALID_HIL_POLICY, "default_hil_level": "ultra"}
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "hil_policy.schema.json")


def test_invalid_hil_policy_bad_policy_version_format() -> None:
    """hil_policy with invalid policy_version format must fail."""
    invalid = {**VALID_HIL_POLICY, "policy_version": "v1.0"}
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "hil_policy.schema.json")


def test_invalid_hil_policy_rule_missing_when() -> None:
    """hil_policy rule without 'when' must fail."""
    invalid = {
        **VALID_HIL_POLICY,
        "rules": [
            {
                "require": {
                    "human_review": "advisory",
                    "auto_approve_if": None,
                }
            }
        ],
    }
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "hil_policy.schema.json")
    assert any("when" in msg for _, msg in exc_info.value.errors)


def test_invalid_hil_policy_rule_missing_require() -> None:
    """hil_policy rule without 'require' must fail."""
    invalid = {
        **VALID_HIL_POLICY,
        "rules": [
            {
                "when": {
                    "phase": "deployment",
                    "agent": None,
                    "task_category": None,
                }
            }
        ],
    }
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "hil_policy.schema.json")
    assert any("require" in msg for _, msg in exc_info.value.errors)


def test_invalid_hil_policy_rule_missing_phase() -> None:
    """hil_policy rule 'when' without 'phase' must fail."""
    invalid = {
        **VALID_HIL_POLICY,
        "rules": [
            {
                "when": {
                    "agent": "claude_code",
                    "task_category": None,
                },
                "require": {
                    "human_review": "advisory",
                    "auto_approve_if": None,
                },
            }
        ],
    }
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "hil_policy.schema.json")
    assert any("phase" in msg for _, msg in exc_info.value.errors)


def test_invalid_hil_policy_rule_empty_phase() -> None:
    """hil_policy rule with empty phase must fail."""
    invalid = {
        **VALID_HIL_POLICY,
        "rules": [
            {
                "when": {
                    "phase": "",
                    "agent": None,
                    "task_category": None,
                },
                "require": {
                    "human_review": "advisory",
                    "auto_approve_if": None,
                },
            }
        ],
    }
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "hil_policy.schema.json")


def test_invalid_hil_policy_bad_human_review_value() -> None:
    """hil_policy with unknown human_review value must fail."""
    invalid = {
        **VALID_HIL_POLICY,
        "rules": [
            {
                "when": {
                    "phase": "deployment",
                    "agent": None,
                    "task_category": None,
                },
                "require": {
                    "human_review": "maybe",
                    "auto_approve_if": None,
                },
            }
        ],
    }
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "hil_policy.schema.json")


def test_invalid_hil_policy_escalation_missing_on_failure_count() -> None:
    """hil_policy escalation without 'on_failure_count' must fail."""
    invalid = {
        **VALID_HIL_POLICY,
        "escalation": {
            "escalate_to": "mandatory",
        },
    }
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "hil_policy.schema.json")
    assert any("on_failure_count" in msg for _, msg in exc_info.value.errors)


def test_invalid_hil_policy_escalation_zero_failure_count() -> None:
    """hil_policy escalation with zero on_failure_count must fail."""
    invalid = {
        **VALID_HIL_POLICY,
        "escalation": {
            "on_failure_count": 0,
            "escalate_to": "mandatory",
        },
    }
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "hil_policy.schema.json")


def test_invalid_hil_policy_escalation_bad_escalate_to() -> None:
    """hil_policy escalation with invalid escalate_to must fail."""
    invalid = {
        **VALID_HIL_POLICY,
        "escalation": {
            "on_failure_count": 3,
            "escalate_to": "none",
        },
    }
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "hil_policy.schema.json")


def test_valid_hil_policy_without_escalation() -> None:
    """hil_policy validates without optional escalation field."""
    valid = {**VALID_HIL_POLICY}
    del valid["escalation"]
    validate_json(valid, "hil_policy.schema.json")


def test_valid_hil_policy_with_auto_approve_condition() -> None:
    """hil_policy validates with auto_approve_if condition."""
    valid = {
        **VALID_HIL_POLICY,
        "rules": [
            {
                "when": {
                    "phase": "code_generation",
                    "agent": "claude_code",
                    "task_category": "refactor",
                },
                "require": {
                    "human_review": "advisory",
                    "auto_approve_if": "tests_pass && coverage>90",
                },
            }
        ],
    }
    validate_json(valid, "hil_policy.schema.json")


def test_valid_hil_policy_minimal() -> None:
    """hil_policy validates with minimal configuration."""
    valid = {
        "policy_version": "1.0",
        "default_hil_level": "none",
        "rules": [
            {
                "when": {
                    "phase": "documentation",
                },
                "require": {
                    "human_review": "none",
                },
            }
        ],
    }
    validate_json(valid, "hil_policy.schema.json")


# ── calibration_metrics negative tests ─────────────────────────────────────

def test_invalid_calibration_metrics_missing_agent_id() -> None:
    """calibration_metrics without 'agent_id' must fail."""
    invalid = {**VALID_CALIBRATION_METRICS}
    del invalid["agent_id"]
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "calibration_metrics.schema.json")
    assert any("agent_id" in msg for _, msg in exc_info.value.errors)


def test_invalid_calibration_metrics_missing_metrics_version() -> None:
    """calibration_metrics without 'metrics_version' must fail."""
    invalid = {**VALID_CALIBRATION_METRICS}
    del invalid["metrics_version"]
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "calibration_metrics.schema.json")
    assert any("metrics_version" in msg for _, msg in exc_info.value.errors)


def test_invalid_calibration_metrics_missing_period() -> None:
    """calibration_metrics without 'period' must fail."""
    invalid = {**VALID_CALIBRATION_METRICS}
    del invalid["period"]
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "calibration_metrics.schema.json")
    assert any("period" in msg for _, msg in exc_info.value.errors)


def test_invalid_calibration_metrics_period_missing_start() -> None:
    """calibration_metrics period without 'start' must fail."""
    invalid = {
        **VALID_CALIBRATION_METRICS,
        "period": {"end": "2026-03-05T23:59:59Z"},
    }
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "calibration_metrics.schema.json")
    assert any("start" in msg for _, msg in exc_info.value.errors)


def test_invalid_calibration_metrics_period_missing_end() -> None:
    """calibration_metrics period without 'end' must fail."""
    invalid = {
        **VALID_CALIBRATION_METRICS,
        "period": {"start": "2026-03-01T00:00:00Z"},
    }
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "calibration_metrics.schema.json")
    assert any("end" in msg for _, msg in exc_info.value.errors)


def test_invalid_calibration_metrics_missing_task_metrics() -> None:
    """calibration_metrics without 'task_metrics' must fail."""
    invalid = {**VALID_CALIBRATION_METRICS}
    del invalid["task_metrics"]
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "calibration_metrics.schema.json")
    assert any("task_metrics" in msg for _, msg in exc_info.value.errors)


def test_invalid_calibration_metrics_missing_human_correction_rate() -> None:
    """calibration_metrics without 'human_correction_rate' must fail."""
    invalid = {**VALID_CALIBRATION_METRICS}
    del invalid["human_correction_rate"]
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "calibration_metrics.schema.json")
    assert any("human_correction_rate" in msg for _, msg in exc_info.value.errors)


def test_invalid_calibration_metrics_human_correction_rate_too_high() -> None:
    """calibration_metrics with human_correction_rate > 1.0 must fail."""
    invalid = {**VALID_CALIBRATION_METRICS, "human_correction_rate": 1.5}
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "calibration_metrics.schema.json")


def test_invalid_calibration_metrics_human_correction_rate_negative() -> None:
    """calibration_metrics with negative human_correction_rate must fail."""
    invalid = {**VALID_CALIBRATION_METRICS, "human_correction_rate": -0.1}
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "calibration_metrics.schema.json")


def test_invalid_calibration_metrics_missing_computed_at() -> None:
    """calibration_metrics without 'computed_at' must fail."""
    invalid = {**VALID_CALIBRATION_METRICS}
    del invalid["computed_at"]
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "calibration_metrics.schema.json")
    assert any("computed_at" in msg for _, msg in exc_info.value.errors)


def test_invalid_calibration_metrics_task_metrics_missing_task_category() -> None:
    """calibration_metrics task_metric without 'task_category' must fail."""
    invalid = {
        **VALID_CALIBRATION_METRICS,
        "task_metrics": [
            {
                "attempts": 100,
                "successes": 85,
                "failures": 15,
                "success_rate": 0.85,
            }
        ],
    }
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "calibration_metrics.schema.json")
    assert any("task_category" in msg for _, msg in exc_info.value.errors)


def test_invalid_calibration_metrics_task_metrics_negative_attempts() -> None:
    """calibration_metrics task_metric with negative attempts must fail."""
    invalid = {
        **VALID_CALIBRATION_METRICS,
        "task_metrics": [
            {
                "task_category": "code_generation",
                "attempts": -1,
                "successes": 85,
                "failures": 15,
                "success_rate": 0.85,
            }
        ],
    }
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "calibration_metrics.schema.json")


def test_invalid_calibration_metrics_task_metrics_success_rate_too_high() -> None:
    """calibration_metrics task_metric with success_rate > 1.0 must fail."""
    invalid = {
        **VALID_CALIBRATION_METRICS,
        "task_metrics": [
            {
                "task_category": "code_generation",
                "attempts": 100,
                "successes": 85,
                "failures": 15,
                "success_rate": 1.5,
            }
        ],
    }
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "calibration_metrics.schema.json")


def test_invalid_calibration_metrics_task_metrics_bad_failure_id() -> None:
    """calibration_metrics task_metric with malformed failure_id must fail."""
    invalid = {
        **VALID_CALIBRATION_METRICS,
        "task_metrics": [
            {
                "task_category": "code_generation",
                "attempts": 100,
                "successes": 85,
                "failures": 15,
                "success_rate": 0.85,
                "failure_types": {
                    "FT-01": 10,  # Wrong format, should be FT-XXX (3 digits)
                },
            }
        ],
    }
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "calibration_metrics.schema.json")


def test_invalid_calibration_metrics_task_metrics_negative_avg_tokens() -> None:
    """calibration_metrics task_metric with negative avg_tokens must fail."""
    invalid = {
        **VALID_CALIBRATION_METRICS,
        "task_metrics": [
            {
                "task_category": "code_generation",
                "attempts": 100,
                "successes": 85,
                "failures": 15,
                "success_rate": 0.85,
                "avg_tokens": -100.0,
            }
        ],
    }
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "calibration_metrics.schema.json")


def test_invalid_calibration_metrics_task_metrics_negative_avg_latency() -> None:
    """calibration_metrics task_metric with negative avg_latency_ms must fail."""
    invalid = {
        **VALID_CALIBRATION_METRICS,
        "task_metrics": [
            {
                "task_category": "code_generation",
                "attempts": 100,
                "successes": 85,
                "failures": 15,
                "success_rate": 0.85,
                "avg_latency_ms": -100.0,
            }
        ],
    }
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "calibration_metrics.schema.json")


def test_valid_calibration_metrics_minimal_task_metric() -> None:
    """calibration_metrics validates with minimal task_metric (only required fields)."""
    valid = {
        **VALID_CALIBRATION_METRICS,
        "task_metrics": [
            {
                "task_category": "code_generation",
                "attempts": 100,
                "successes": 85,
                "failures": 15,
                "success_rate": 0.85,
            }
        ],
    }
    validate_json(valid, "calibration_metrics.schema.json")


def test_valid_calibration_metrics_empty_failure_types() -> None:
    """calibration_metrics validates with empty failure_types object."""
    valid = {
        **VALID_CALIBRATION_METRICS,
        "task_metrics": [
            {
                "task_category": "code_generation",
                "attempts": 100,
                "successes": 100,
                "failures": 0,
                "success_rate": 1.0,
                "failure_types": {},
            }
        ],
    }
    validate_json(valid, "calibration_metrics.schema.json")


def test_valid_calibration_metrics_empty_task_metrics() -> None:
    """calibration_metrics validates with empty task_metrics array."""
    valid = {
        **VALID_CALIBRATION_METRICS,
        "task_metrics": [],
    }
    validate_json(valid, "calibration_metrics.schema.json")


def test_valid_calibration_metrics_zero_human_correction_rate() -> None:
    """calibration_metrics validates with zero human_correction_rate."""
    valid = {**VALID_CALIBRATION_METRICS, "human_correction_rate": 0.0}
    validate_json(valid, "calibration_metrics.schema.json")


def test_valid_calibration_metrics_max_human_correction_rate() -> None:
    """calibration_metrics validates with human_correction_rate = 1.0."""
    valid = {**VALID_CALIBRATION_METRICS, "human_correction_rate": 1.0}
    validate_json(valid, "calibration_metrics.schema.json")


# ── gates_ledger negative tests ────────────────────────────────────────────

def test_invalid_gates_ledger_missing_ledger_version() -> None:
    """gates_ledger without 'ledger_version' must fail."""
    invalid = {**VALID_GATES_LEDGER}
    del invalid["ledger_version"]
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "gates_ledger.schema.json")
    assert any("ledger_version" in msg for _, msg in exc_info.value.errors)


def test_invalid_gates_ledger_missing_run_id() -> None:
    """gates_ledger without 'run_id' must fail."""
    invalid = {**VALID_GATES_LEDGER}
    del invalid["run_id"]
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "gates_ledger.schema.json")
    assert any("run_id" in msg for _, msg in exc_info.value.errors)


def test_invalid_gates_ledger_missing_entries() -> None:
    """gates_ledger without 'entries' must fail."""
    invalid = {**VALID_GATES_LEDGER}
    del invalid["entries"]
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "gates_ledger.schema.json")
    assert any("entries" in msg for _, msg in exc_info.value.errors)


def test_invalid_gates_ledger_missing_integrity_hash() -> None:
    """gates_ledger without 'integrity_hash' must fail."""
    invalid = {**VALID_GATES_LEDGER}
    del invalid["integrity_hash"]
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "gates_ledger.schema.json")
    assert any("integrity_hash" in msg for _, msg in exc_info.value.errors)


def test_invalid_gates_ledger_bad_run_id() -> None:
    """gates_ledger with non-hex run_id must fail."""
    invalid = {**VALID_GATES_LEDGER, "run_id": "ZZZZZZZZ"}
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "gates_ledger.schema.json")


def test_invalid_gates_ledger_bad_ledger_version_format() -> None:
    """gates_ledger with non-semver ledger_version must fail."""
    invalid = {**VALID_GATES_LEDGER, "ledger_version": "v1.0"}
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "gates_ledger.schema.json")


def test_invalid_gates_ledger_bad_integrity_hash() -> None:
    """gates_ledger with malformed integrity_hash must fail."""
    invalid = {**VALID_GATES_LEDGER, "integrity_hash": "not-a-hash"}
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "gates_ledger.schema.json")


def test_invalid_gates_ledger_entry_missing_sequence_number() -> None:
    """gates_ledger entry without 'sequence_number' must fail."""
    invalid = {
        **VALID_GATES_LEDGER,
        "entries": [
            {
                "gate_receipt": VALID_GATES_LEDGER["entries"][0]["gate_receipt"],
            }
        ],
    }
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "gates_ledger.schema.json")
    assert any("sequence_number" in msg for _, msg in exc_info.value.errors)


def test_invalid_gates_ledger_entry_missing_gate_receipt() -> None:
    """gates_ledger entry without 'gate_receipt' must fail."""
    invalid = {
        **VALID_GATES_LEDGER,
        "entries": [
            {
                "sequence_number": 1,
            }
        ],
    }
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "gates_ledger.schema.json")
    assert any("gate_receipt" in msg for _, msg in exc_info.value.errors)


def test_invalid_gates_ledger_entry_zero_sequence_number() -> None:
    """gates_ledger entry with sequence_number = 0 must fail."""
    invalid = {
        **VALID_GATES_LEDGER,
        "entries": [
            {
                "sequence_number": 0,
                "gate_receipt": VALID_GATES_LEDGER["entries"][0]["gate_receipt"],
            }
        ],
    }
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "gates_ledger.schema.json")


def test_invalid_gates_ledger_entry_negative_sequence_number() -> None:
    """gates_ledger entry with negative sequence_number must fail."""
    invalid = {
        **VALID_GATES_LEDGER,
        "entries": [
            {
                "sequence_number": -1,
                "gate_receipt": VALID_GATES_LEDGER["entries"][0]["gate_receipt"],
            }
        ],
    }
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "gates_ledger.schema.json")


def test_valid_gates_ledger_empty_entries() -> None:
    """gates_ledger validates with empty entries array."""
    valid = {**VALID_GATES_LEDGER, "entries": []}
    validate_json(valid, "gates_ledger.schema.json")


def test_valid_gates_ledger_single_entry() -> None:
    """gates_ledger validates with single entry."""
    valid = {
        **VALID_GATES_LEDGER,
        "entries": [VALID_GATES_LEDGER["entries"][0]],
    }
    validate_json(valid, "gates_ledger.schema.json")


def test_valid_gates_ledger_minimal() -> None:
    """gates_ledger validates with minimal required fields."""
    valid = {
        "schema_version": "gates-ledger/1.0",
        "ledger_version": "1.0.0",
        "run_id": "abcd1234",
        "entries": [],
        "integrity_hash": "sha256:" + "0" * 64,
    }
    validate_json(valid, "gates_ledger.schema.json")


def test_valid_gates_ledger_with_canonical_metadata_fields() -> None:
    """gates_ledger validates with explicit canonical_fields and metadata_fields."""
    valid = {
        **VALID_GATES_LEDGER,
        "canonical_fields": ["/entries", "/integrity_hash", "/ledger_version"],
        "metadata_fields": ["/run_id"],
    }
    validate_json(valid, "gates_ledger.schema.json")


# ── calibration_report tests ───────────────────────────────────────────────

def test_valid_calibration_report() -> None:
    """calibration_report validates with all required fields."""
    validate_json(VALID_CALIBRATION_REPORT, "calibration_report.schema.json")


def test_invalid_calibration_report_missing_report_id() -> None:
    """calibration_report without 'report_id' must fail."""
    invalid = {**VALID_CALIBRATION_REPORT}
    del invalid["report_id"]
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "calibration_report.schema.json")
    assert any("report_id" in msg for _, msg in exc_info.value.errors)


def test_invalid_calibration_report_missing_summary() -> None:
    """calibration_report without 'summary' must fail."""
    invalid = {**VALID_CALIBRATION_REPORT}
    del invalid["summary"]
    with pytest.raises(SchemaValidationError) as exc_info:
        validate_json(invalid, "calibration_report.schema.json")
    assert any("summary" in msg for _, msg in exc_info.value.errors)


def test_invalid_calibration_report_bad_uuid() -> None:
    """calibration_report with malformed report_id is accepted (format validation not enforced).

    Note: The jsonschema validator does not enforce format validation by default.
    This test documents the current behavior. UUID format is advisory in the schema.
    """
    invalid = {**VALID_CALIBRATION_REPORT, "report_id": "not-a-uuid"}
    # Currently passes - format validation is not enforced
    validate_json(invalid, "calibration_report.schema.json")


def test_invalid_calibration_report_success_rate_out_of_range() -> None:
    """calibration_report with success_rate > 1.0 must fail."""
    invalid = {**VALID_CALIBRATION_REPORT}
    invalid["summary"] = {**invalid["summary"], "success_rate": 1.5}
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "calibration_report.schema.json")


def test_invalid_calibration_report_negative_total_tasks() -> None:
    """calibration_report with negative total_tasks must fail."""
    invalid = {**VALID_CALIBRATION_REPORT}
    invalid["summary"] = {**invalid["summary"], "total_tasks": -1}
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "calibration_report.schema.json")


def test_valid_calibration_report_empty_agent_breakdown() -> None:
    """calibration_report validates with empty agent_breakdown array."""
    valid = {**VALID_CALIBRATION_REPORT, "agent_breakdown": []}
    validate_json(valid, "calibration_report.schema.json")


def test_valid_calibration_report_empty_routing_performance() -> None:
    """calibration_report validates with empty routing_performance array."""
    valid = {**VALID_CALIBRATION_REPORT, "routing_performance": []}
    validate_json(valid, "calibration_report.schema.json")


def test_valid_calibration_report_empty_recommendations() -> None:
    """calibration_report validates with empty recommendations array."""
    valid = {**VALID_CALIBRATION_REPORT, "recommendations": []}
    validate_json(valid, "calibration_report.schema.json")


def test_valid_calibration_report_empty_failure_distribution() -> None:
    """calibration_report validates with empty failure_distribution object."""
    valid = {**VALID_CALIBRATION_REPORT, "failure_distribution": {}}
    validate_json(valid, "calibration_report.schema.json")


def test_invalid_calibration_report_hil_rate_out_of_range() -> None:
    """calibration_report with hil_rate > 1.0 must fail."""
    invalid = {**VALID_CALIBRATION_REPORT}
    invalid["hil_metrics"] = {**invalid["hil_metrics"], "hil_rate": 1.5}
    with pytest.raises(SchemaValidationError):
        validate_json(invalid, "calibration_report.schema.json")
