{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://coderifts.com/schemas/preflight-response.v2.consumer.json",
  "title": "CodeRifts Preflight Response v2 \u2014 CONSUMER (discriminated on preflight_mode)",
  "description": "Permissive CONSUMER view of Decision Spec 2.0 preflight success body. Structural source of truth for mode discrimination is preflight-response.v2.producer.json (mirror: same oneOf on preflight_mode). ANALYZE cannot contain decision, execution_action, safe_for_agent, chain_receipt, execution_grant, decision_result, control_envelope, or required_action_core (structural omission \u2014 not permission). Analyze issues no receipt and no authorization, so every authorization artifact is forbidden on that branch, not merely absent; the branch stays additionalProperties:true so genuinely-additive NON-authorization fields keep validating. AUTHORIZE requires decision + execution_action (+ safe_for_agent) and may carry chain_receipt when authorized (execution_grant only when opted in). Additive fields may appear and are not permission. Aligns with live buildChangeSet / MCP preflight_change_set outputSchema. Which field is canonical? On authorize responses that include the control surface: control_envelope is the branch source (agents / agent-guard read execution_action there); top-level decision, safe_for_agent, and execution_action are compatibility mirrors of the same values (never conflicting dual truths); decision_result is the proof/receipt side (envelope + body_hash + receipt), not a second branch enum. Prefer control_envelope.execution_action for branching.",
  "oneOf": [
    {
      "type": "object",
      "title": "Analyze preflight (informational)",
      "properties": {
        "preflight_mode": {
          "const": "analyze"
        },
        "analysis_outcome": {
          "type": "string",
          "enum": [
            "NO_BREAK_DETECTED",
            "BREAKS_DETECTED",
            "ANALYSIS_FAILED"
          ]
        },
        "authorization_effect": {
          "const": "NONE"
        },
        "may_execute": {
          "const": false
        },
        "receipt_kind": {
          "const": "NONE"
        },
        "decision_spec_version": {
          "type": "string"
        },
        "risk_score": {
          "type": "integer",
          "minimum": 0,
          "maximum": 100
        },
        "breaking_changes": {
          "type": "integer",
          "minimum": 0
        },
        "patterns": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "bundle_fingerprint": {
          "type": "string"
        },
        "verdict_fingerprint": {
          "type": "string"
        },
        "artifacts": {
          "type": "array"
        },
        "evidence": {
          "type": "array"
        },
        "meta": {
          "type": "object"
        },
        "correlation_id": {
          "type": "string"
        },
        "analysis_control": {
          "type": "object"
        },
        "detected_patterns": {
          "type": "array",
          "description": "GOVERNANCE detector detail rows (src/change-patterns.js; validated by decision-spec-fields.js). Row shape measured live: name, severity, description, consequence, affected_path, affected_field; optional side (request|response) on ENUM_NARROWING. Names \u2286 patterns (not equality). Agent-detector names may appear only in patterns. Free-text fields are untrusted."
        },
        "breaking_changes_details": {
          "type": "array",
          "description": "Per-change IR/detail rows from the engine (src/blast/diff-to-change.js maps these). Measured row keys: type, path, method, field, severity, description. Distinct from breaking_changes (integer count)."
        },
        "severity_summary": {
          "type": "object",
          "description": "Bundle severity axes (src/change-set.js severity_summary). Distinct axes, not contradictory. Measured keys: diff_severity, governance_severity, policy_effect, note."
        },
        "analysis": {
          "type": "object",
          "description": "Tier-2 analysis mirror (src/response-envelope.js buildAnalysisTier / attachControlSurface). Dual-write of flat analysis fields present on the verdict plus remediations[]. ANALYSIS_TIER_FIELDS subset when present; not a closed property set."
        },
        "human_report": {
          "type": "object",
          "description": "Human-readable report tier (src/response-envelope.js buildHumanReport / analyze v2 stub). Measured keys: summary, breaking_highlights, suggestions, next_steps_prose."
        },
        "request_correlation_id": {
          "type": "string",
          "description": "Gateway/request correlation id (attachControlSurface). Set when correlation_id is present; alias of the request id. Distinct from decision_correlation_id on authorize when the envelope has its own id."
        },
        "scorer_version": {
          "type": [
            "string",
            "null"
          ]
        },
        "calibration_version": {
          "type": [
            "string",
            "null"
          ]
        },
        "policy_pin_status": {
          "type": [
            "object",
            "null"
          ]
        },
        "blast_radius": {
          "type": "object",
          "description": "ID27 additive COUNTS (not a score). Not permission.",
          "properties": {
            "endpoints": { "type": "integer", "minimum": 0 },
            "fields": { "type": "integer", "minimum": 0 },
            "params": { "type": "integer", "minimum": 0 },
            "consumers_declared": { "type": "integer", "minimum": 0 },
            "consumers_observed": { "type": "integer", "minimum": 0 },
            "graph_source": { "type": "string" }
          }
        }
      },
      "required": [
        "preflight_mode",
        "analysis_outcome",
        "authorization_effect",
        "may_execute",
        "receipt_kind",
        "decision_spec_version"
      ],
      "not": {
        "anyOf": [
          {
            "required": [
              "decision"
            ]
          },
          {
            "required": [
              "execution_action"
            ]
          },
          {
            "required": [
              "safe_for_agent"
            ]
          },
          {
            "required": [
              "chain_receipt"
            ]
          },
          {
            "required": [
              "execution_grant"
            ]
          },
          {
            "required": [
              "decision_result"
            ]
          },
          {
            "required": [
              "control_envelope"
            ]
          },
          {
            "required": [
              "required_action_core"
            ]
          }
        ]
      },
      "additionalProperties": true
    },
    {
      "type": "object",
      "title": "Authorize preflight (operation-bound)",
      "properties": {
        "preflight_mode": {
          "const": "authorize"
        },
        "decision": {
          "type": "string",
          "enum": [
            "ALLOW",
            "WARN",
            "REQUIRE_APPROVAL",
            "BLOCK"
          ]
        },
        "execution_action": {
          "type": "string",
          "enum": [
            "CONTINUE",
            "CONTINUE_WITH_MONITORING",
            "REQUEST_APPROVAL",
            "STOP"
          ]
        },
        "safe_for_agent": {
          "type": "boolean"
        },
        "receipt_kind": {
          "type": "string",
          "enum": [
            "operation_authorization",
            "NONE"
          ]
        },
        "chain_receipt": {
          "type": "string"
        },
        "execution_grant": {
          "type": "string",
          "description": "Opt-in cr.exec.v1 execution grant. Absent unless requested on authorize. Optional inner state_nonce (ATOMIC profile) is additive and is NOT in scope_hash. See docs/cr-exec-v1.md / docs/cr-exec-attest-v1.md."
        },
        "decision_result": {
          "type": "object"
        },
        "decision_spec_version": {
          "type": "string"
        },
        "risk_score": {
          "type": "integer",
          "minimum": 0,
          "maximum": 100
        },
        "breaking_changes": {
          "type": "integer",
          "minimum": 0
        },
        "control_envelope": {
          "type": "object",
          "description": "Branch source (control/1.0). Includes next_agent_step (structured remediation SUGGESTION derived from execution_action + required_action; null on CONTINUE*; not permission \u2014 still branch on execution_action). See control-envelope.v1.producer.json for nested shape."
        },
        "meta": {
          "type": "object"
        },
        "detected_patterns": {
          "type": "array",
          "description": "GOVERNANCE detector detail rows (src/change-patterns.js; validated by decision-spec-fields.js). Row shape measured live: name, severity, description, consequence, affected_path, affected_field; optional side (request|response) on ENUM_NARROWING. Names \u2286 patterns (not equality). Agent-detector names may appear only in patterns. Free-text fields are untrusted."
        },
        "breaking_changes_details": {
          "type": "array",
          "description": "Per-change IR/detail rows from the engine (src/blast/diff-to-change.js maps these). Measured row keys: type, path, method, field, severity, description. Distinct from breaking_changes (integer count)."
        },
        "severity_summary": {
          "type": "object",
          "description": "Bundle severity axes (src/change-set.js severity_summary). Distinct axes, not contradictory. Measured keys: diff_severity, governance_severity, policy_effect, note."
        },
        "analysis": {
          "type": "object",
          "description": "Tier-2 analysis mirror (src/response-envelope.js buildAnalysisTier / attachControlSurface). Dual-write of flat analysis fields present on the verdict plus remediations[]. ANALYSIS_TIER_FIELDS subset when present; not a closed property set."
        },
        "human_report": {
          "type": "object",
          "description": "Human-readable report tier (src/response-envelope.js buildHumanReport / analyze v2 stub). Measured keys: summary, breaking_highlights, suggestions, next_steps_prose."
        },
        "request_correlation_id": {
          "type": "string",
          "description": "Gateway/request correlation id (attachControlSurface). Set when correlation_id is present; alias of the request id. Distinct from decision_correlation_id on authorize when the envelope has its own id."
        },
        "decision_correlation_id": {
          "type": "string",
          "description": "Decision envelope correlation id (attachControlSurface). Present on authorize when decision_result.correlation_id is set. Distinct from request_correlation_id / correlation_id (request gateway id). Measured authorize-only."
        },
        "scorer_version": {
          "type": [
            "string",
            "null"
          ]
        },
        "calibration_version": {
          "type": [
            "string",
            "null"
          ]
        },
        "policy_pin_status": {
          "type": [
            "object",
            "null"
          ]
        },
        "blast_radius": {
          "type": "object",
          "description": "ID27 additive COUNTS (not a score). Not permission.",
          "properties": {
            "endpoints": { "type": "integer", "minimum": 0 },
            "fields": { "type": "integer", "minimum": 0 },
            "params": { "type": "integer", "minimum": 0 },
            "consumers_declared": { "type": "integer", "minimum": 0 },
            "consumers_observed": { "type": "integer", "minimum": 0 },
            "graph_source": { "type": "string" }
          }
        }
      },
      "required": [
        "preflight_mode",
        "decision",
        "execution_action",
        "safe_for_agent",
        "receipt_kind",
        "decision_spec_version"
      ],
      "allOf": [
        {
          "if": {
            "properties": {
              "receipt_kind": {
                "const": "operation_authorization"
              }
            },
            "required": [
              "receipt_kind"
            ]
          },
          "then": {
            "required": [
              "chain_receipt"
            ]
          }
        },
        {
          "if": {
            "properties": {
              "execution_action": {
                "enum": [
                  "CONTINUE",
                  "CONTINUE_WITH_MONITORING"
                ]
              }
            },
            "required": [
              "execution_action"
            ]
          },
          "then": {
            "properties": {
              "receipt_kind": {
                "const": "operation_authorization"
              }
            },
            "required": [
              "receipt_kind",
              "chain_receipt",
              "decision_result"
            ]
          }
        }
      ],
      "additionalProperties": true
    }
  ]
}
