{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "AI Consultant Response Envelope v3",
  "description": "Envelope written for every consultant (<consultant>.json), whether the answer was structured, usable prose (fallback) or a failure (error). Built by the response builders in scripts/lib/common.sh; the provider answer is canonicalized by scripts/lib/response_canonicalize.jq first. The synthesis artifact (synthesis.json) is definitions.synthesis_artifact. Checked in tests with scripts/lib/json_schema_check.jq.",
  "type": "object",
  "required": ["consultant", "model", "persona", "response", "confidence", "metadata"],
  "properties": {
    "consultant": {
      "type": "string",
      "minLength": 1,
      "description": "Consultant name: Gemini, Codex, Mistral, Kimi, Claude, Qwen3, GLM, Grok, DeepSeek, MiniMax, Meta, or a custom API agent (title-cased from ENABLE_<NAME>)."
    },
    "model": {
      "type": "string",
      "description": "Best effective model identifier (see metadata.model_identity_source for the strength of that evidence)."
    },
    "persona": {
      "type": "string",
      "description": "Persona display name: a catalog name (scripts/lib/personas.sh), {AGENT}_PERSONA_NAME, 'Custom Persona', or 'External Consultant'."
    },
    "response": {
      "type": "object",
      "required": ["summary", "detailed", "approach"],
      "properties": {
        "summary": {
          "type": "string",
          "description": "TL;DR in 2-3 sentences (the contract asks for at most 500 characters; not truncated locally). May be blank, which yields no summary finding."
        },
        "detailed": { "type": "string", "description": "Full response. Never atomized into coverage findings." },
        "approach": {
          "type": "string",
          "description": "Approach name. 'unspecified' when the consultant gave none, 'unstructured-provider-response' for fallback prose, 'error' for failures."
        },
        "pros": { "type": "array", "items": { "type": "string" } },
        "cons": { "type": "array", "items": { "type": "string" } },
        "caveats": { "type": "array", "items": { "type": "string" } },
        "references": { "type": "array", "items": { "type": "string" } },
        "alternatives": {
          "type": "array",
          "description": "Strings, or objects with name and reason_not_chosen.",
          "items": { "type": ["string", "object"] }
        },
        "code_snippets": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "language": { "type": "string" },
              "code": { "type": "string" },
              "description": { "type": "string" }
            }
          }
        },
        "findings": {
          "type": "array",
          "description": "Added locally during synthesis to successful responses. Provider-supplied findings/IDs are never trusted.",
          "items": { "$ref": "#/definitions/normalized_finding" }
        }
      }
    },
    "confidence": {
      "type": "object",
      "required": ["score", "reasoning"],
      "properties": {
        "score": {
          "type": "integer",
          "minimum": 0,
          "maximum": 10,
          "description": "1-10 for answers (fallback prose carries a synthetic 5); 0 only in error envelopes."
        },
        "reasoning": { "type": "string" },
        "uncertainty_factors": { "type": "array", "items": { "type": "string" } }
      }
    },
    "metadata": {
      "type": "object",
      "required": ["tokens_used", "tokens_source", "latency_ms", "model_version", "requested_model", "model_identity_source", "response_quality", "timestamp"],
      "properties": {
        "tokens_used": { "type": "integer", "minimum": 0, "description": "Tokens consumed; read tokens_source before treating it as exact." },
        "tokens_input": { "type": "integer", "minimum": 0, "description": "Prompt tokens (measured or estimated, see tokens_source). Preferred over re-splitting tokens_used." },
        "tokens_output": { "type": "integer", "minimum": 0, "description": "Completion tokens (measured or estimated, see tokens_source)." },
        "tokens_source": {
          "type": "string",
          "enum": ["measured", "estimated", "unknown"],
          "description": "measured = provider usage (API, Claude CLI, Codex CLI telemetry); estimated = local 4-chars-per-token approximation; unknown = not recorded."
        },
        "tokens_cached_input": { "type": "integer", "minimum": 0, "description": "Cached subset of tokens_input (Codex CLI); not added again." },
        "provider_cost_usd": { "type": "number", "minimum": 0, "description": "Provider-reported cost (Claude CLI); preferred over any estimate." },
        "billing_models": { "type": "array", "items": { "type": "string" }, "description": "Billing participants (Claude CLI); not content-model attestation." },
        "cost_source": {
          "type": "string",
          "enum": ["unavailable", "estimated-standard-rates", "estimated-long-context-standard-rates"],
          "description": "Codex estimate provenance; not a provider invoice."
        },
        "cost_note": { "type": "string" },
        "latency_ms": { "type": "integer", "minimum": 0 },
        "model_version": { "type": "string" },
        "requested_model": { "type": "string" },
        "model_identity_source": { "type": "string", "enum": ["provider-reported", "capability-probed", "requested-only"] },
        "response_quality": {
          "type": "string",
          "enum": ["structured", "fallback", "error", "unknown"],
          "description": "structured = the answer satisfied the JSON contract (after canonicalization); fallback = usable prose kept as context only; error = failed or malformed; unknown = legacy."
        },
        "error": { "type": "string", "description": "Failure reason (error envelopes)." },
        "raw_excerpt": { "type": "string", "description": "First 2000 characters of a rejected (malformed or schema-invalid) provider answer, for diagnosis." },
        "transport": { "type": "string", "enum": ["cli", "api", "api_fallback"], "description": "Route that answered (Grok)." },
        "cli_version": { "type": "string", "description": "Observed CLI version, provenance only." },
        "cli_compatibility": { "type": "string", "enum": ["capability-probed", "incompatible"] },
        "timestamp": { "type": "string", "minLength": 1, "description": "ISO 8601 timestamp." }
      }
    },
    "cache_metadata": {
      "type": "object",
      "description": "Present on responses written to or replayed from the semantic cache.",
      "required": ["cached_at", "fingerprint", "from_cache"],
      "properties": {
        "cached_at": { "type": "string" },
        "fingerprint": { "type": "string" },
        "from_cache": { "type": "boolean" }
      }
    }
  },
  "definitions": {
    "normalized_finding": {
      "type": "object",
      "required": ["id", "kind", "field", "text"],
      "properties": {
        "id": {
          "type": "string",
          "pattern": "^[a-z0-9_]+:[1-9][0-9]*$",
          "description": "Stable local ID: normalized lowercase consultant slug (alphanumeric/underscore) plus one-based deterministic index."
        },
        "kind": { "type": "string", "enum": ["summary", "recommendation", "trade_off", "alternative", "edge_case", "evidence"] },
        "field": { "type": "string", "enum": ["summary", "pros", "cons", "alternatives", "caveats", "references"] },
        "text": { "type": "string", "minLength": 1 }
      }
    },
    "synthesis_coverage_item": {
      "type": "object",
      "required": ["point", "source_ids"],
      "properties": {
        "point": { "type": "string" },
        "raised_by": { "type": "array", "items": { "type": "string" } },
        "source_ids": {
          "type": "array",
          "minItems": 1,
          "items": { "type": "string", "pattern": "^[a-z0-9_]+:[1-9][0-9]*$" },
          "description": "Required local normalized source IDs. The local integrity check removes IDs outside the expected response findings."
        },
        "kind": { "type": "string" }
      }
    },
    "coverage_integrity": {
      "type": "object",
      "required": ["status", "expected_count", "represented_count", "missing_ids", "unknown_ids", "duplicate_ids", "non_normalizable_consultants", "structural_errors", "audited_fields", "normalization_version", "disclosure"],
      "properties": {
        "status": { "type": "string", "enum": ["MET", "DEGRADED", "FAILED", "NOT_APPLICABLE"] },
        "expected_count": { "type": "integer", "minimum": 0 },
        "represented_count": { "type": "integer", "minimum": 0 },
        "missing_ids": { "type": "array", "items": { "type": "string" } },
        "unknown_ids": { "type": "array", "items": { "type": "string" } },
        "duplicate_ids": { "type": "array", "items": { "type": "string" } },
        "non_normalizable_consultants": { "type": "array", "items": { "type": "string" } },
        "structural_errors": { "type": "array", "items": { "type": "string" } },
        "audited_fields": { "type": "array", "items": { "type": "string" } },
        "normalization_version": { "type": "string" },
        "disclosure": { "type": "string" }
      }
    },
    "synthesis_artifact": {
      "type": "object",
      "description": "synthesis.json. Presentation fields are canonicalized by scripts/lib/synthesis_canonicalize.jq; strategy, timestamp, consultants_analyzed, coverage_integrity, coverage_input_truncated, truncated_consultants, synthesis_provider and synthesis_metadata are always local, never model-supplied.",
      "required": ["synthesis_version", "timestamp", "strategy", "consultants_analyzed", "coverage", "weighted_recommendation", "risk_assessment", "action_items", "follow_up_questions", "coverage_integrity", "coverage_input_truncated", "truncated_consultants", "synthesis_provider"],
      "properties": {
        "synthesis_version": { "type": "string" },
        "timestamp": { "type": "string", "minLength": 1 },
        "strategy": { "type": "string" },
        "consultants_analyzed": { "type": "integer", "minimum": 0 },
        "coverage": { "type": "array", "items": { "$ref": "#/definitions/synthesis_coverage_item" } },
        "weighted_recommendation": {
          "type": "object",
          "required": ["approach", "summary", "detailed"],
          "properties": {
            "approach": { "type": "string" },
            "summary": { "type": "string" },
            "detailed": { "type": "string" },
            "supporting_consultants": { "type": "array", "items": { "type": "string" } },
            "incorporated_insights": { "type": "array", "items": { "type": "string" } }
          }
        },
        "risk_assessment": {
          "type": "object",
          "required": ["overall_risk", "risks"],
          "properties": {
            "overall_risk": { "type": "string", "enum": ["low", "medium", "high", "unknown"] },
            "risks": {
              "type": "array",
              "items": {
                "type": "object",
                "required": ["description", "severity", "mitigation"],
                "properties": {
                  "description": { "type": "string", "minLength": 1 },
                  "severity": { "type": "string", "enum": ["low", "medium", "high", "unknown"] },
                  "mitigation": { "type": "string" },
                  "identified_by": { "type": "array", "items": { "type": "string" } }
                }
              }
            }
          }
        },
        "action_items": {
          "type": "array",
          "items": {
            "type": "object",
            "required": ["priority", "action", "rationale"],
            "properties": {
              "priority": { "type": "integer", "minimum": 1 },
              "action": { "type": "string", "minLength": 1 },
              "rationale": { "type": "string" }
            }
          }
        },
        "follow_up_questions": { "type": "array", "items": { "type": "string" } },
        "coverage_integrity": { "$ref": "#/definitions/coverage_integrity" },
        "coverage_input_truncated": { "type": "boolean" },
        "truncated_consultants": { "type": "array", "items": { "type": "string" } },
        "synthesis_provider": { "type": "string", "description": "claude, gemini, codex, or local-fallback." },
        "synthesis_metadata": {
          "type": "object",
          "required": ["provider", "model", "tokens_input", "tokens_output", "tokens_source", "estimated_cost_usd"],
          "properties": {
            "provider": { "type": "string" },
            "model": { "type": "string" },
            "tokens_input": { "type": "integer", "minimum": 0 },
            "tokens_output": { "type": "integer", "minimum": 0 },
            "tokens_source": { "type": "string", "enum": ["estimated"] },
            "estimated_cost_usd": { "type": "number", "minimum": 0 }
          }
        },
        "fallback": { "type": "boolean" }
      }
    }
  }
}
