# GLADIATOR contest definition schema, v1 (JSON Schema 2020-12, YAML surface).
# A contest file is the single declarative input of a category run. An
# incomplete file is refused before any action.
$schema: "https://json-schema.org/draft/2020-12/schema"
$id: "https://github.com/devlollc/gladiator/schemas/contest.v1.yaml"
title: "GLADIATOR contest definition, v1"
type: object
additionalProperties: false
required:
  - contest_version
  - category
  - promise
  - task
  - dataset
  - metrics
  - participants
  - budget
  - window
  - scoring
  - freshness
  - conflicts
  - limits
properties:
  contest_version:
    const: "contest.v1"
  category:
    type: string
    pattern: "^[a-z0-9][a-z0-9_-]*$"
  promise:
    type: string
    minLength: 1
    description: "The goal the tools themselves promise, measured as promised."
  task:
    type: string
    minLength: 1
  context:
    type: string
  dataset:
    type: object
    additionalProperties: false
    required: [description, size, frozen_at, sha256, contains_personal_data]
    properties:
      description: { type: string, minLength: 1 }
      size: { type: integer, minimum: 1 }
      frozen_at: { $ref: "#/$defs/utc_datetime" }
      sha256: { $ref: "#/$defs/sha256" }
      contains_personal_data: { type: boolean }
  metrics:
    type: object
    additionalProperties: false
    required: [primary]
    properties:
      primary: { type: string, minLength: 1 }
      secondary:
        type: array
        items: { type: string }
  participants:
    type: array
    minItems: 2
    items: { $ref: "#/$defs/participant" }
  budget:
    type: object
    additionalProperties: false
    required: [cap_usd]
    properties:
      cap_usd: { type: number, minimum: 0 }
      per_participant_cap_usd: { type: number, minimum: 0 }
  window:
    type: object
    additionalProperties: false
    required: [start, end]
    properties:
      start: { $ref: "#/$defs/utc_datetime" }
      end: { $ref: "#/$defs/utc_datetime" }
  scoring:
    type: object
    additionalProperties: false
    required: [min_n, max_interval_width, confidence, min_blocks_for_rank]
    properties:
      min_n: { type: integer, minimum: 1 }
      max_interval_width:
        type: number
        exclusiveMinimum: 0
        maximum: 1
      confidence: { type: number, exclusiveMinimum: 0, exclusiveMaximum: 1 }
      min_blocks_for_rank: { type: integer, minimum: 1 }
  freshness:
    type: object
    additionalProperties: false
    required: [validity_days]
    properties:
      validity_days: { type: integer, minimum: 1 }
  assignment:
    type: object
    additionalProperties: false
    required: [method, seed]
    properties:
      method: { type: string, minLength: 1 }
      seed: { type: [integer, string] }
  conflicts:
    type: array
    items: { type: string }
  limits:
    type: array
    minItems: 1
    items: { type: string, minLength: 1 }
  runner:
    type: string
  funding:
    type: string
$defs:
  utc_datetime:
    type: string
    pattern: "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?Z$"
  sha256:
    type: string
    pattern: "^[0-9a-f]{64}$"
  participant:
    type: object
    additionalProperties: false
    required: [name]
    properties:
      name: { type: string, pattern: "^[a-z0-9][a-z0-9_-]*$" }
      vendor: { type: string }
      excluded: { type: boolean }
      devlo_linked: { type: boolean }
      exclusion_reason: { type: string }
      access:
        type: object
        additionalProperties: false
        properties:
          api: { type: boolean }
          cli: { type: boolean }
          mcp: { type: boolean }
          auth: { type: string }
          setup_minutes: { type: number, minimum: 0 }
    allOf:
      # Any devlo-linked product (Elson included) can only appear excluded,
      # with a written reason. Neutrality is structural, not declarative.
      - if:
          properties:
            devlo_linked: { const: true }
          required: [devlo_linked]
        then:
          required: [excluded, exclusion_reason]
          properties:
            excluded: { const: true }
