{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://ainumbers.co/research/engineering-decision-schema/schemas/episode.schema.json",
  "title": "Engineering Decision Episode",
  "description": "One recorded expert decision in a physical-design or proving flow: the state before, what was observed, what was considered and rejected, what was done, the state after, and the expert's judgment. Three document kinds share this repository's schema set (episode / run manifest / zkprof report).",
  "type": "object",
  "required": [
    "episode_id",
    "provenance",
    "design",
    "prior_state",
    "observation",
    "hypothesis",
    "candidates_considered",
    "chosen_intervention",
    "post_state",
    "expert_assessment",
    "rationale",
    "confidence",
    "failure_class"
  ],
  "additionalProperties": false,
  "properties": {
    "$schema": {
      "type": "string",
      "description": "Optional self-identifying reference to this schema. Lets edi-validate pick the document kind without an explicit --type."
    },
    "episode_id": {
      "type": "string",
      "minLength": 1,
      "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$",
      "description": "Stable unique id. No whitespace. Convention: EP-<domain>-<nnn>."
    },
    "provenance": {
      "type": "string",
      "enum": ["retrospective", "reproduced", "synthetic-fixture"],
      "description": "retrospective = recalled from a prior tapeout, no run artifact. reproduced = regenerated by a committed flow run. synthetic-fixture = schema-valid validator food, NOT data."
    },
    "design": {
      "type": "object",
      "required": ["name", "pdk"],
      "additionalProperties": false,
      "properties": {
        "name": { "type": "string", "minLength": 1 },
        "rtl_commit": { "type": "string", "minLength": 1 },
        "pdk": { "type": "string", "minLength": 1 },
        "pdk_commit": { "type": "string", "minLength": 1 }
      }
    },
    "environment": {
      "type": "object",
      "required": ["librelane_version", "openroad_version", "yosys_version"],
      "additionalProperties": false,
      "properties": {
        "librelane_version": { "type": "string", "minLength": 1 },
        "openroad_version": { "type": "string", "minLength": 1 },
        "yosys_version": { "type": "string", "minLength": 1 },
        "container": { "type": "string", "minLength": 1 }
      },
      "description": "Flow tool versions. Required when provenance is 'reproduced'. Note: LibreLane (never 'OpenLane') with the OpenROAD P&R engine."
    },
    "prior_state": { "$ref": "#/$defs/state" },
    "post_state": { "$ref": "#/$defs/state" },
    "observation": {
      "type": "object",
      "required": ["metric", "value", "target", "severity"],
      "additionalProperties": false,
      "properties": {
        "metric": {
          "type": "string",
          "minLength": 1,
          "description": "Metric name with unit suffix, e.g. wns_ns, wirelength_um, overflow_pct."
        },
        "value": {
          "description": "Observed value. Number, or a string when the observation is not scalar.",
          "type": ["number", "string"]
        },
        "target": {
          "description": "The value the design was aiming for. Number or string.",
          "type": ["number", "string"]
        },
        "severity": {
          "type": "string",
          "enum": ["blocking", "degrading", "cosmetic"]
        }
      }
    },
    "hypothesis": {
      "type": "string",
      "minLength": 1,
      "description": "The expert's causal reading of the observation. Free text; judgment field."
    },
    "candidates_considered": {
      "type": "array",
      "minItems": 1,
      "description": "Every intervention considered, including the rejected ones. Exactly one entry MUST have rejected_because: null -- that one is the chosen intervention. Enforced by the Python validator (cross-field), not expressible in JSON Schema.",
      "items": { "$ref": "#/$defs/candidate" }
    },
    "chosen_intervention": {
      "type": "object",
      "required": ["parameter", "before", "after"],
      "additionalProperties": false,
      "properties": {
        "parameter": { "type": "string", "minLength": 1 },
        "before": { "type": ["number", "string", "null"] },
        "after": { "type": ["number", "string", "null"] }
      }
    },
    "expert_assessment": {
      "type": "string",
      "enum": ["preferred", "acceptable", "regretted", "inconclusive"]
    },
    "rationale": {
      "type": "string",
      "minLength": 1,
      "description": "Why the chosen intervention was taken over the others. Judgment field."
    },
    "confidence": {
      "type": "number",
      "minimum": 0,
      "maximum": 1
    },
    "failure_class": {
      "type": "string",
      "enum": [
        "FAIL_CONGESTION",
        "FAIL_TIMING",
        "FAIL_IR_DROP",
        "FAIL_ANTENNA",
        "FAIL_DRC",
        "FAIL_LVS",
        "FAIL_ROUTER",
        "FAIL_CTS",
        "FAIL_MEMORY",
        "FAIL_FLOW",
        "SDC_BUDGET"
      ]
    },
    "run_manifest_ref": {
      "type": "string",
      "minLength": 1,
      "description": "Optional reference to the run-manifest document backing a reproduced episode (run_id or path)."
    },
    "schema_version": {
      "type": "string",
      "minLength": 1,
      "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$",
      "description": "Version identifier of the episode schema document this record was written against (e.g. 0.2.0). Optional: a record written before this field existed carries no value and still validates. Lets a stored episode be told apart from one written under a future enum/field set."
    },
    "x_extension": {
      "type": "object",
      "description": "The ONE sanctioned extension slot. Every x_-prefixed, repository-specific or capture-only field lives here (e.g. x_capture metadata from the questionnaire), since the schema is otherwise closed (additionalProperties: false). Container sees no validation inside; the extension is not part of the episode contract.",
      "additionalProperties": true
    }
  },
  "allOf": [
    {
      "title": "reproduced episodes must carry an environment and pinned commits",
      "if": {
        "type": "object",
        "properties": { "provenance": { "const": "reproduced" } },
        "required": ["provenance"]
      },
      "then": {
        "required": ["environment"],
        "properties": {
          "design": {
            "type": "object",
            "required": ["rtl_commit", "pdk_commit"]
          }
        }
      }
    }
  ],
  "$defs": {
    "state": {
      "type": "object",
      "required": ["stage", "metrics"],
      "additionalProperties": false,
      "properties": {
        "stage": {
          "type": "string",
          "minLength": 1,
          "description": "Flow stage label, e.g. global_placement, detailed_routing, cts."
        },
        "metrics": {
          "type": "object",
          "description": "Open object. Units live in the key names (wns_ns, wirelength_um). A metric that was not measured at this stage MUST be absent, never present as 0.",
          "additionalProperties": { "type": ["number", "string"] }
        }
      }
    },
    "candidate": {
      "type": "object",
      "required": ["intervention", "rejected_because"],
      "additionalProperties": false,
      "properties": {
        "intervention": { "type": "string", "minLength": 1 },
        "rejected_because": {
          "description": "Why this candidate was not taken. null marks the candidate that was chosen.",
          "type": ["string", "null"]
        }
      }
    }
  }
}
