{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://codafort.dev/schemas/coda-crash-v1.schema.json",
  "title": "coda-crash/1",
  "description": "Envelope da modalidade CRASH (emitido pelo codacrash, forense de dump/binário). O nome segue a convenção coda-<modalidade>/1 (ADR-0002): o contrato é nomeado pelo QUE ele carrega, não por qual binário o emite. UMA modalidade com SUB-MODALIDADES COMO DADO: os quatro laudos do domínio de crash — análise de um dump, triagem de um lote, heap dump gerenciado e hs_err da JVM — convergiram para esta tag, discriminados pelo campo `kind` (decisão do owner, 2026-07-29). Forense de performance NÃO entra aqui: ela é `coda-profile/1`, modalidade própria. Determinístico: o mesmo artefato produz o mesmo envelope byte-a-byte — é isso que torna a foto (golden) possível. PUBLICADO em 2026-09-13 a partir dos emissores reais (`codacrash/crates/cli/src/report/`), que escrevem o JSON à mão, sem serde: o contrato descreve o que o binário DE FATO emite, e o gate de conformidade ao lado do emissor impede que os dois se separem.",
  "type": "object",
  "required": ["schema", "kind"],
  "properties": {
    "schema": { "const": "coda-crash/1" },
    "detail": {
      "type": "object",
      "description": "O laudo de análise completo da família do dump (sinal, registradores, pilha, módulos, avisos) — a peça que o texto mostra e o envelope resume. Forma por família: core ELF, minidump, core Mach-O. Presente no kind analysis desde 2026-09-25, quando o --format json passou de um fluxo de objetos a um documento só."
    },
    "native_heap": {
      "type": "object",
      "description": "Forense do heap nativo glibc/ptmalloc: limites, chunks caminhados, corrupção estrutural, o tcache de cada thread (chunks LIBERADOS e anomalias: double free, escrita após free, cabeçalho sobrescrito) e fault_in_freed_chunk quando o endereço da falha cai num chunk liberado."
    },
    "guardlog": {
      "type": "object",
      "description": "Resumo da proveniência que o agente codaguard deixou no core (bloco SPGUARD1): allocações rastreadas, eventos, erros, tetos do build e deny-list."
    },
    "crash_fn": {
      "type": "object",
      "description": "A função do crash desmontada: chamadas de PLT e strings de .rodata que ela referencia."
    },
    "kind": {
      "enum": ["analysis", "triage", "heap", "hserr", "correlation"],
      "description": "A sub-modalidade, como DADO. `analysis` = um dump analisado; `triage` = um lote agrupado por CRASH_ID; `heap` = heap dump de runtime gerenciado (Java/V8); `hserr` = o `hs_err_pid.log` da JVM; `correlation` = N dumps comparados campo a campo (RF-70)."
    },
    "tool": {
      "type": "object",
      "description": "Proveniência: quem emitiu e em que versão. **Ausente nos envelopes de hoje** — os emissores do codacrash são anteriores ao ADR-0002, que manda o produto viajar aqui. Declarado como OPCIONAL para que o contrato descreva a realidade sem impedir a correção: quando o emissor passar a preenchê-lo, nenhum consumidor quebra. A divergência com o ADR está registrada para a Mesa.",
      "required": ["name", "version"],
      "properties": {
        "name": { "type": "string", "minLength": 1 },
        "version": { "type": "string", "minLength": 1 }
      }
    },

    "os": { "type": "string", "description": "`windows` · `linux` · `macos`, como o dump declarou." },
    "arch": { "type": "string", "description": "Arquitetura do processo (`x64`, `x86`, `arm64`, …)." },
    "fault": { "type": "string", "description": "Rótulo da falha, como o formato a nomeia (`EXCEPTION_ACCESS_VIOLATION (0xc0000005)`, `11 SIGSEGV`)." },
    "fault_address": {
      "type": "integer",
      "description": "Endereço efetivo da falha. **Ausente quando o dump não o carrega** — campo ausente não é zero, e zero aqui significa ponteiro NULO."
    },
    "crash_id": {
      "type": "string",
      "description": "Impressão digital de 8 hex do crash (Fase B): mesma causa ⇒ mesmo id, entre máquinas e execuções. É a chave de agrupamento da triagem e a identidade que a plataforma usa para recorrência."
    },
    "severity": {
      "enum": ["critical", "high", "medium", "low", "unknown"],
      "description": "Escala CANÔNICA do coda-finding/1, nunca o rótulo pt-BR da saída de texto: campo de máquina não carrega string localizada."
    },
    "exploitability": {
      "enum": ["high", "medium", "low", "unknown"],
      "description": "Classificação estilo `!exploitable` (Fase E). `unknown` é resultado legítimo — e é diferente de `low`."
    },
    "cwe": {
      "type": "array",
      "items": { "type": "integer" },
      "description": "Classe(s) de weakness do fault — a MESMA chave de junção da correlação run→src. Ausente quando o fault não classifica (ausência ≠ lista vazia por decisão: o emissor omite o campo)."
    },
    "fault_insn": {
      "type": "object",
      "additionalProperties": false,
      "required": ["access", "effective_addr", "target"],
      "description": "A instrução que falhou, quando o desassemblador a alcança.",
      "properties": {
        "access": { "type": "string", "description": "`R` · `W` · `X` — o acesso que a instrução tentou." },
        "effective_addr": { "type": "integer" },
        "target": { "type": "string", "description": "Descrição do alvo em inglês (`NULL pointer`, `heap`), para o humano que lê o laudo." }
      }
    },
    "heap_corrupt": {
      "type": ["boolean", "null"],
      "description": "**`null` quando indeterminado, não `false`** (I4): o laudo distingue 'olhei e o heap está íntegro' de 'não consegui olhar'. É a diferença que sustenta o campo num atestado."
    },
    "stack_smash": { "type": "boolean", "description": "Canário de pilha violado (stack smashing detectado)." },

    "dumps": { "type": "integer", "description": "`kind: triage` — quantos dumps entraram no lote." },
    "cores": {
      "type": "array",
      "description": "`kind: correlation` — a assinatura de cada dump comparado, na ordem da linha de comando.",
      "items": {
        "type": "object",
        "additionalProperties": false,
        "required": ["file", "os", "fault", "top_frame", "heap", "heap_corrupt", "error_clue"],
        "properties": {
          "file": { "type": "string" },
          "os": { "type": "string" },
          "fault": { "type": "string" },
          "top_frame": { "type": "string", "description": "Primeiro frame resolvido em módulo, ou `?`." },
          "heap": { "type": "string", "description": "Estado do heap, ou `n/d` quando não foi lido." },
          "heap_corrupt": { "type": "boolean", "description": "O heap foi lido e acusou corrupção." },
          "error_clue": { "type": ["string", "null"] }
        }
      }
    },
    "common": {
      "type": "object",
      "description": "`kind: correlation` — igualdade crua campo a campo entre os dumps. Não é veredito: `n/d` igual a `n/d` sai `true` aqui e não conta no `verdict`.",
      "additionalProperties": { "type": "boolean" }
    },
    "verdict": {
      "enum": ["same_defect", "probable_same_defect", "divergent"],
      "description": "`kind: correlation` — o julgamento. Só valor MEDIDO que caracteriza defeito conta: topo resolvido idêntico e/ou heap corrompido com a mesma assinatura."
    },
    "groups": {
      "type": "array",
      "description": "`kind: triage` — os grupos por CRASH_ID, ordenados. Um grupo com `count` alto é recorrência, que é o sinal que a plataforma persegue.",
      "items": {
        "type": "object",
        "additionalProperties": false,
        "required": ["crash_id", "count", "files"],
        "properties": {
          "crash_id": { "type": "string" },
          "count": { "type": "integer" },
          "os": { "type": "string" },
          "fault": { "type": "string" },
          "top_frame": { "type": "string" },
          "heap": { "type": "string", "description": "Estado do heap no grupo, ou `n/d` quando não determinado." },
          "files": { "type": "array", "items": { "type": "string" }, "description": "Os artefatos daquele grupo, por nome de arquivo." }
        }
      }
    },

    "runtime": { "type": "string", "description": "`kind: heap` — o motor do heap dump (`java`, `v8`, …). É o eixo do Finding canônico para esta sub-modalidade." },
    "heap": {
      "type": "object",
      "additionalProperties": false,
      "required": ["objects", "bytes", "classes", "roots"],
      "description": "`kind: heap` — totais do heap.",
      "properties": {
        "objects": { "type": "integer" },
        "bytes": { "type": "integer" },
        "classes": { "type": "integer" },
        "roots": { "type": "integer" }
      }
    },
    "histogram": {
      "type": "array",
      "description": "`kind: heap` — objetos por classe, do mais pesado ao mais leve.",
      "items": {
        "type": "object",
        "required": ["class", "instances", "bytes"],
        "properties": {
          "class": { "type": "string" },
          "instances": { "type": "integer" },
          "bytes": { "type": "integer" }
        }
      }
    },
    "leak_suspects": {
      "type": "array",
      "description": "`kind: heap` — suspeitos de retenção, com a cadeia até a GC root. A cadeia é o que transforma 'este objeto é grande' em 'este objeto é grande POR CAUSA DAQUELE'.",
      "items": {
        "type": "object",
        "required": ["class", "id", "retained", "shallow"],
        "properties": {
          "class": { "type": "string" },
          "id": { "type": "integer" },
          "retained": { "type": "integer" },
          "shallow": { "type": "integer" },
          "gc_root": { "type": "string" },
          "chain": {
            "type": "array",
            "items": {
              "type": "object",
              "required": ["class", "id"],
              "properties": { "class": { "type": "string" }, "id": { "type": "integer" } }
            }
          }
        }
      }
    },
    "anti_patterns": { "type": "array", "description": "`kind: heap` — padrões conhecidos de retenção (ClassLoader retido, coleção/array gigante)." },
    "truncated": { "type": "boolean", "description": "`kind: heap` — o laudo foi cortado por orçamento. Truncado é dito, nunca silencioso." },
    "warnings": { "type": "array", "items": { "type": "string" }, "description": "Avisos do parser: o que ele não conseguiu ler, em vez de fingir que leu." },

    "signal": { "type": "string", "description": "`kind: hserr` — o sinal fatal (`SIGSEGV (0xb)`)." },
    "fatal_reason": { "type": ["string", "null"], "description": "`kind: hserr` — razão fatal declarada pela JVM (OOM, `guarantee` falhada). `null` quando o log não a traz." },
    "pc": { "type": "string", "description": "`kind: hserr` — program counter no momento da falha." },
    "pid": { "type": "integer" },
    "problematic_frame": { "type": "string", "description": "`kind: hserr` — o frame que a própria JVM aponta como problemático." },
    "vm": { "type": "string", "description": "`kind: hserr` — identificação da VM, como o log a escreve." },
    "native_frames": { "type": "array", "items": { "type": "string" } },
    "java_frames": { "type": "array", "items": { "type": "string" } },
    "deadlock": {
      "type": ["object", "null"],
      "description": "`kind: hserr` — ciclo de espera entre threads, quando existe. `null` = não há deadlock NO LOG, o que não é o mesmo que 'a aplicação não trava'.",
      "properties": {
        "reported_by_jvm": { "type": "boolean", "description": "`true` = quem afirmou o deadlock foi a própria JVM; `false` = a inferência é do codacrash." },
        "cycle": {
          "type": "array",
          "items": {
            "type": "object",
            "required": ["thread"],
            "properties": {
              "thread": { "type": "string" },
              "waiting_for": { "type": "string" },
              "held_by": { "type": "string" }
            }
          }
        }
      }
    }
  },
  "allOf": [
    {
      "if": { "properties": { "kind": { "const": "analysis" } }, "required": ["kind"] },
      "then": { "required": ["os", "arch", "fault", "crash_id", "severity", "exploitability", "heap_corrupt", "stack_smash"] }
    },
    {
      "if": { "properties": { "kind": { "const": "triage" } }, "required": ["kind"] },
      "then": { "required": ["dumps", "groups"] }
    },
    {
      "if": { "properties": { "kind": { "const": "correlation" } }, "required": ["kind"] },
      "then": { "required": ["cores", "common", "verdict"] }
    },
    {
      "if": { "properties": { "kind": { "const": "heap" } }, "required": ["kind"] },
      "then": { "required": ["runtime", "heap", "histogram", "leak_suspects", "anti_patterns", "truncated", "warnings"] }
    },
    {
      "if": { "properties": { "kind": { "const": "hserr" } }, "required": ["kind"] },
      "then": { "required": ["signal", "pc", "pid", "native_frames", "java_frames"] }
    }
  ]
}
