{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://pickled.dev/schema/pickled.schema.json",
  "title": "pickled.yml",
  "description": "Configuration for testing whether agents understand a product (v2). Editor/tooling contract; the pickled loader remains the authoritative validator (it owns cross-references, mode-specific rules, and provider-specific rules).",
  "type": "object",
  "required": ["schemaVersion", "product", "agents", "contexts"],
  "additionalProperties": false,
  "properties": {
    "schemaVersion": {
      "const": 2,
      "description": "Schema version. Must be 2."
    },
    "product": { "$ref": "#/$defs/product" },
    "sources": { "$ref": "#/$defs/sources" },
    "agents": { "$ref": "#/$defs/agents" },
    "contexts": { "$ref": "#/$defs/contexts" },
    "facts": { "$ref": "#/$defs/facts" },
    "misstatements": { "$ref": "#/$defs/misstatements" },
    "questions": { "$ref": "#/$defs/questions" },
    "builds": { "$ref": "#/$defs/builds" },
    "thresholds": { "$ref": "#/$defs/thresholds" }
  },
  "$defs": {
    "product": {
      "type": "object",
      "description": "Name and describe the product being tested.",
      "required": ["name", "description"],
      "additionalProperties": false,
      "properties": {
        "name": {
          "type": "string",
          "minLength": 1,
          "description": "Product name shown in prompts and reports.",
          "examples": ["my-product"]
        },
        "description": {
          "type": "string",
          "minLength": 1,
          "description": "Short product description used to orient the agent.",
          "examples": ["A CLI for managing cloud deployments"]
        }
      }
    },
    "sources": {
      "type": "object",
      "description": "Named context artifacts, keyed by id. Each declares exactly one kind: url, path, or codebase.",
      "additionalProperties": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "url": {
            "type": "string",
            "description": "A fetched URL source.",
            "examples": ["https://docs.example.com/llms.txt"]
          },
          "path": {
            "type": "string",
            "description": "A local file source.",
            "examples": ["./README.md"]
          },
          "codebase": {
            "type": "string",
            "description": "A glob over a code tree, loaded as one source.",
            "examples": ["./src/**/*.ts"]
          },
          "exclude": {
            "type": "array",
            "description": "codebase only: globs to exclude.",
            "items": { "type": "string", "minLength": 1 }
          },
          "maxBytes": {
            "type": "integer",
            "minimum": 1,
            "description": "codebase only: max total bytes loaded."
          }
        }
      }
    },
    "agent": {
      "type": "object",
      "required": ["provider", "model"],
      "additionalProperties": false,
      "properties": {
        "provider": {
          "enum": ["claude-code", "codex-cli", "anthropic", "openai"],
          "description": "Agent provider or interface to run."
        },
        "model": {
          "type": "string",
          "minLength": 1,
          "description": "Model identifier to run. Pin a specific version; aliases can drift across SDK upgrades.",
          "examples": ["claude-haiku-4-5", "gpt-5.2"]
        },
        "temperature": {
          "type": "number",
          "description": "Sampling temperature for API agents."
        },
        "maxTokens": {
          "type": "integer",
          "minimum": 1,
          "description": "Maximum response tokens for API agents."
        },
        "maxTurns": {
          "type": "integer",
          "minimum": 1,
          "description": "Maximum turns for CLI agents that support a turn cap."
        }
      }
    },
    "agents": {
      "type": "object",
      "description": "Agents or model interfaces that run each task.",
      "minProperties": 1,
      "additionalProperties": { "$ref": "#/$defs/agent" }
    },
    "mcpServer": {
      "type": "object",
      "required": ["url"],
      "additionalProperties": false,
      "properties": {
        "url": {
          "type": "string",
          "pattern": "^https?://",
          "description": "HTTP MCP server URL.",
          "examples": ["https://docs.example.com/mcp"]
        },
        "headers": {
          "type": "object",
          "description": "Optional HTTP headers. ${VAR} values are expanded from the environment by the loader.",
          "additionalProperties": { "type": "string" }
        }
      }
    },
    "context": {
      "type": "object",
      "required": ["mode"],
      "additionalProperties": false,
      "description": "How a source reaches the agent. The loader enforces per-mode rules (memory: no source/servers; inject: source required; web: source optional; mcp: servers required).",
      "properties": {
        "mode": {
          "enum": ["memory", "inject", "web", "mcp"],
          "description": "memory (prior knowledge), inject (source content placed in the prompt), web (agent uses web tools), mcp (agent uses MCP servers)."
        },
        "source": {
          "type": "string",
          "minLength": 1,
          "description": "A registered source id."
        },
        "servers": {
          "type": "object",
          "description": "mcp only: MCP servers, keyed by label.",
          "minProperties": 1,
          "additionalProperties": { "$ref": "#/$defs/mcpServer" }
        }
      }
    },
    "contexts": {
      "type": "object",
      "description": "Named delivery paths: how (or whether) a source reaches the agent.",
      "minProperties": 1,
      "additionalProperties": { "$ref": "#/$defs/context" }
    },
    "match": {
      "type": "object",
      "description": "Deterministic substring match. Satisfied when all allOf and at least one anyOf appear. Declare at least one side.",
      "additionalProperties": false,
      "properties": {
        "allOf": {
          "type": "array",
          "minItems": 1,
          "description": "Substrings that must all appear.",
          "items": { "type": "string", "minLength": 1 }
        },
        "anyOf": {
          "type": "array",
          "minItems": 1,
          "description": "Substrings of which at least one must appear.",
          "items": { "type": "string", "minLength": 1 }
        }
      },
      "anyOf": [{ "required": ["allOf"] }, { "required": ["anyOf"] }]
    },
    "matchEntry": {
      "type": "object",
      "required": ["statement", "match"],
      "additionalProperties": false,
      "properties": {
        "statement": {
          "type": "string",
          "minLength": 1,
          "description": "Human-readable statement shown in receipts."
        },
        "match": { "$ref": "#/$defs/match" }
      }
    },
    "facts": {
      "type": "object",
      "description": "Reusable product truths an answer must cover (coverage axis), keyed by id.",
      "additionalProperties": { "$ref": "#/$defs/matchEntry" }
    },
    "misstatements": {
      "type": "object",
      "description": "Reusable wrong claims an answer must not make (precision axis), keyed by id.",
      "additionalProperties": { "$ref": "#/$defs/matchEntry" }
    },
    "examples": {
      "type": "object",
      "description": "Offline sample answers checked by pickled test (no model calls). Required when a question declares rejects.",
      "additionalProperties": false,
      "properties": {
        "pass": {
          "type": "array",
          "description": "Answers that should score YES.",
          "items": { "type": "string", "minLength": 1 }
        },
        "fail": {
          "type": "array",
          "description": "Answers that should not score YES (and must trip a rejected misstatement when rejects is set).",
          "items": { "type": "string", "minLength": 1 }
        }
      }
    },
    "question": {
      "type": "object",
      "required": ["id", "question", "agents", "contexts"],
      "additionalProperties": false,
      "description": "A source-legibility probe: scored on fact coverage (expects) and misstatement rejection (rejects). At least one of expects/rejects is required.",
      "properties": {
        "id": {
          "type": "string",
          "minLength": 1,
          "description": "Stable task id used in reports and CLI filters (--task)."
        },
        "question": {
          "type": "string",
          "minLength": 1,
          "description": "The question asked of the agent."
        },
        "agents": {
          "type": "array",
          "minItems": 1,
          "description": "Agent ids declared in the top-level agents map.",
          "items": { "type": "string", "minLength": 1 }
        },
        "contexts": {
          "type": "array",
          "minItems": 1,
          "description": "Context ids declared in the top-level contexts map.",
          "items": { "type": "string", "minLength": 1 }
        },
        "expects": {
          "type": "array",
          "description": "Fact ids the answer must cover.",
          "items": { "type": "string", "minLength": 1 }
        },
        "rejects": {
          "type": "array",
          "description": "Misstatement ids the answer must not make (hard veto).",
          "items": { "type": "string", "minLength": 1 }
        },
        "examples": { "$ref": "#/$defs/examples" }
      }
    },
    "questions": {
      "type": "array",
      "description": "Source-legibility probes.",
      "items": { "$ref": "#/$defs/question" }
    },
    "workspace": {
      "type": "object",
      "required": ["path"],
      "additionalProperties": false,
      "description": "The throwaway fixture a build edits.",
      "properties": {
        "path": {
          "type": "string",
          "minLength": 1,
          "description": "Fixture directory the agent edits, relative to pickled.yml."
        },
        "setup": {
          "type": "array",
          "description": "Commands run to prepare the fixture before the agent.",
          "items": { "type": "string", "minLength": 1 }
        }
      }
    },
    "command": {
      "type": "object",
      "required": ["run"],
      "additionalProperties": false,
      "properties": {
        "name": {
          "type": "string",
          "minLength": 1,
          "description": "Optional receipt label; defaults to run."
        },
        "run": {
          "type": "string",
          "minLength": 1,
          "description": "Shell command. Passes on exit 0."
        }
      }
    },
    "verifier": {
      "type": "object",
      "required": ["failToPass"],
      "additionalProperties": false,
      "description": "SWE-bench-style verifier: failToPass must fail on the untouched fixture and pass after; passToPass must pass before and after.",
      "properties": {
        "failToPass": {
          "type": "array",
          "minItems": 1,
          "items": { "$ref": "#/$defs/command" }
        },
        "passToPass": {
          "type": "array",
          "items": { "$ref": "#/$defs/command" }
        }
      }
    },
    "build": {
      "type": "object",
      "required": ["id", "goal", "agents", "contexts", "workspace", "verifier"],
      "additionalProperties": false,
      "description": "An implementation proof: the agent edits a workspace and the verifier scores it, k/n over trials.",
      "properties": {
        "id": { "type": "string", "minLength": 1 },
        "goal": {
          "type": "string",
          "minLength": 1,
          "description": "The work the agent must do."
        },
        "agents": {
          "type": "array",
          "minItems": 1,
          "items": { "type": "string", "minLength": 1 }
        },
        "contexts": {
          "type": "array",
          "minItems": 1,
          "items": { "type": "string", "minLength": 1 }
        },
        "trials": {
          "type": "integer",
          "minimum": 1,
          "description": "Independent trials per cell (default 1)."
        },
        "requires": {
          "type": "array",
          "description": "Fact ids linked for diagnostic reporting.",
          "items": { "type": "string", "minLength": 1 }
        },
        "workspace": { "$ref": "#/$defs/workspace" },
        "verifier": { "$ref": "#/$defs/verifier" },
        "referenceSolution": {
          "type": "object",
          "required": ["patch"],
          "additionalProperties": false,
          "description": "Optional positive control: a patch that must pass the verifier on the baseline.",
          "properties": {
            "patch": {
              "type": "string",
              "minLength": 1,
              "description": "Path to a patch file."
            }
          }
        }
      }
    },
    "builds": {
      "type": "array",
      "description": "Implementation proofs.",
      "items": { "$ref": "#/$defs/build" }
    },
    "thresholds": {
      "type": "object",
      "description": "Per-kind run gates. Omit a key for no gate.",
      "additionalProperties": false,
      "properties": {
        "questions": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "description": "Minimum questions score (1-100) for pickled check to pass."
        },
        "builds": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "description": "Minimum builds score (1-100) for pickled build to pass."
        }
      }
    }
  }
}
