{
  "$id": "https://codafort.dev/schemas/coda-fort-v1.schema.json",
  "$schema": "http://json-schema.org/draft-07/schema#",
  "definitions": {
    "AllocSite": {
      "description": "Site abstrato de allocation (linha + var alvo). Usamos linha como\nsurrogate de alloc-site ID.\n\n**Sprint 11**: opcionalmente referencia `HeapAlloc.id` via `heap_id`,\npermitindo joins entre as views Andersen e heap_builder.",
      "properties": {
        "assigned_to": {
          "type": "string"
        },
        "heap_id": {
          "default": null,
          "format": "uint32",
          "minimum": 0,
          "type": [
            "integer",
            "null"
          ]
        },
        "line": {
          "format": "uint32",
          "minimum": 0,
          "type": "integer"
        },
        "method_id": {
          "$ref": "#/definitions/MethodId"
        }
      },
      "required": [
        "method_id",
        "line",
        "assigned_to"
      ],
      "type": "object"
    },
    "BlameInfo": {
      "description": "**Refino A.4 — blame per-linha.** Autor (commit) que introduziu/alterou por\núltimo a linha de um finding, na cadeia first-parent a partir do HEAD.\nAtribuição precisa de autoria POR FINDING — eleva a anomalia histórico-autoral\nde module-level p/ line-level. Só populado sob `--git`; `None` caso contrário.",
      "properties": {
        "author": {
          "type": "string"
        },
        "commit": {
          "description": "SHA (40 hex) do commit que por último tocou a linha.",
          "type": "string"
        },
        "committed_unix": {
          "description": "Tempo do autor (segundos desde a epoch).",
          "format": "int64",
          "type": "integer"
        },
        "email": {
          "type": "string"
        }
      },
      "required": [
        "author",
        "email",
        "commit",
        "committed_unix"
      ],
      "type": "object"
    },
    "BlockId": {
      "format": "uint32",
      "minimum": 0,
      "type": "integer"
    },
    "CallSiteId": {
      "format": "uint32",
      "minimum": 0,
      "type": "integer"
    },
    "Confidence": {
      "enum": [
        "High",
        "Medium",
        "Low"
      ],
      "type": "string"
    },
    "CpgNodeKind": {
      "description": "Tipos de nós do Code Property Graph. Vocabulário inspirado no Joern\n(`io.shiftleft.codepropertygraph.generated.NodeTypes`), **restrito ao que os\nfrontends produzem de fato** (auditoria 2026-07-18): kinds declarados sem\nnenhum produtor nos 16 `.tsg` foram removidos — paridade nominal sem\nimplementação viola a honestidade do schema. Reintroduzir custa 1 linha\nquando um produtor real surgir.",
      "oneOf": [
        {
          "enum": [
            "File",
            "Namespace",
            "TypeDecl",
            "Method",
            "MethodParameterIn",
            "Member",
            "Local",
            "ControlStructure",
            "Literal",
            "Call",
            "Return",
            "Import"
          ],
          "type": "string"
        },
        {
          "const": "Unknown",
          "description": "Fallback de `parse_node_kind` para `ast_kind` desconhecido.",
          "type": "string"
        }
      ]
    },
    "DiagnosticKind": {
      "enum": [
        "ParseError",
        "MissingFrontendFeature",
        "Other"
      ],
      "type": "string"
    },
    "DiagnosticLevel": {
      "enum": [
        "Info",
        "Warn",
        "Error"
      ],
      "type": "string"
    },
    "Entrypoint": {
      "description": "**PC-1.2 (plan-progressive-confirmation)** — ponto de entrada HTTP do handler que o fluxo atravessa: o verbo (`GET`…\nou `ANY`) e o caminho declarado na rota (`/cmdi-00/BenchmarkTest00006`, `/api/users/{id}`), sem o contexto do deploy.\nLeva o finding estático ao teste dinâmico dirigido e à junção com o DAST.",
      "properties": {
        "method": {
          "type": "string"
        },
        "path": {
          "type": "string"
        }
      },
      "required": [
        "method",
        "path"
      ],
      "type": "object"
    },
    "FileId": {
      "format": "uint32",
      "minimum": 0,
      "type": "integer"
    },
    "FindingTier": {
      "description": "**F1a (plan-duas-camadas, 2026-07-11)** — camada do veredito de um finding.\n`Confirmed`: fluxo provado — entra na conta de precisão e é assinável pelo\nattest. `Suspected`: candidato de baixa confiança recuperado **sem tocar** a\nprecisão do confirmed (triado pelo agente). Serializa só quando `Suspected`\n(o `confirmed` é o default silencioso → golden snapshots inalterados).",
      "enum": [
        "confirmed",
        "suspected"
      ],
      "type": "string"
    },
    "FlowStep": {
      "description": "**codeFlows (degrau 2 \"mostrar\"):** um passo do caminho source→sink de um\nfinding de taint. Renderizado no SARIF `codeFlows` (visível no GitHub Code\nScanning / IDE) e no envelope. Vazio para findings não-taint.",
      "properties": {
        "file": {
          "type": "string"
        },
        "line": {
          "format": "uint32",
          "minimum": 0,
          "type": "integer"
        },
        "message": {
          "description": "Rótulo do passo (ex.: \"source: env.var()\", \"sink: Command.new()\").",
          "type": "string"
        }
      },
      "required": [
        "file",
        "line",
        "message"
      ],
      "type": "object"
    },
    "HeapAllocKind": {
      "enum": [
        "object",
        "array",
        "string"
      ],
      "type": "string"
    },
    "HirStmtKind": {
      "oneOf": [
        {
          "properties": {
            "kind": {
              "const": "assignment",
              "type": "string"
            },
            "source": {
              "type": "string"
            },
            "target": {
              "type": "string"
            }
          },
          "required": [
            "kind",
            "target",
            "source"
          ],
          "type": "object"
        },
        {
          "properties": {
            "args": {
              "items": {
                "type": "string"
              },
              "type": "array"
            },
            "callee": {
              "type": "string"
            },
            "kind": {
              "const": "call",
              "type": "string"
            }
          },
          "required": [
            "kind",
            "callee",
            "args"
          ],
          "type": "object"
        },
        {
          "properties": {
            "kind": {
              "const": "return",
              "type": "string"
            },
            "value": {
              "type": [
                "string",
                "null"
              ]
            }
          },
          "required": [
            "kind"
          ],
          "type": "object"
        },
        {
          "description": "**Sprint 12**: branch agora carrega ranges de consequence/alternative\npara que consumidores downstream possam navegar a estrutura sem\nre-parsear. Ranges são `[start_line, end_line]` (1-based, inclusivos).",
          "properties": {
            "alternative": {
              "default": null,
              "items": {
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "maxItems": 2,
              "minItems": 2,
              "type": [
                "array",
                "null"
              ]
            },
            "condition": {
              "type": "string"
            },
            "consequence": {
              "default": null,
              "items": {
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "maxItems": 2,
              "minItems": 2,
              "type": [
                "array",
                "null"
              ]
            },
            "kind": {
              "const": "branch",
              "type": "string"
            }
          },
          "required": [
            "kind",
            "condition"
          ],
          "type": "object"
        },
        {
          "properties": {
            "kind": {
              "const": "throw",
              "type": "string"
            },
            "value": {
              "type": "string"
            }
          },
          "required": [
            "kind",
            "value"
          ],
          "type": "object"
        },
        {
          "properties": {
            "kind": {
              "const": "break",
              "type": "string"
            }
          },
          "required": [
            "kind"
          ],
          "type": "object"
        },
        {
          "properties": {
            "kind": {
              "const": "continue",
              "type": "string"
            }
          },
          "required": [
            "kind"
          ],
          "type": "object"
        },
        {
          "properties": {
            "kind": {
              "const": "expression",
              "type": "string"
            },
            "text": {
              "type": "string"
            }
          },
          "required": [
            "kind",
            "text"
          ],
          "type": "object"
        },
        {
          "properties": {
            "kind": {
              "const": "unknown",
              "type": "string"
            }
          },
          "required": [
            "kind"
          ],
          "type": "object"
        }
      ]
    },
    "InterprocEdge": {
      "description": "Edge de fluxo de dados que cruza a fronteira de uma função.",
      "properties": {
        "call_line": {
          "description": "Linha do call site no caller.",
          "format": "uint32",
          "minimum": 0,
          "type": "integer"
        },
        "callee_method": {
          "$ref": "#/definitions/MethodId"
        },
        "caller_method": {
          "$ref": "#/definitions/MethodId"
        },
        "from_var": {
          "description": "Origem: arg (no caller) p/ `ArgToParam`; var retornada (no callee) p/\n`ReturnToResult`.",
          "type": "string"
        },
        "kind": {
          "$ref": "#/definitions/InterprocEdgeKind"
        },
        "to_var": {
          "description": "Destino: param (no callee) p/ `ArgToParam`; lhs/resultado (no caller) p/\n`ReturnToResult`.",
          "type": "string"
        }
      },
      "required": [
        "caller_method",
        "callee_method",
        "call_line",
        "kind",
        "from_var",
        "to_var"
      ],
      "type": "object"
    },
    "InterprocEdgeKind": {
      "oneOf": [
        {
          "const": "ArgToParam",
          "description": "Arg do caller flui para o parâmetro posicional do callee.",
          "type": "string"
        },
        {
          "const": "ReturnToResult",
          "description": "Var retornada pelo callee flui para o resultado (lhs) no caller.",
          "type": "string"
        }
      ]
    },
    "IssueKind": {
      "enum": [
        "Bug",
        "Vulnerability",
        "CodeSmell",
        "SecurityHotspot"
      ],
      "type": "string"
    },
    "IssueResolution": {
      "oneOf": [
        {
          "enum": [
            "Fixed",
            "FalsePositive",
            "WontFix",
            "Removed"
          ],
          "type": "string"
        },
        {
          "const": "NotAffected",
          "description": "Sprint 69 (VEX): marcado not-affected/fixed por um statement VEX\n(`.codafort-vex.yaml`) — vulnerabilidade conhecida mas declarada não\nexplorável neste contexto.",
          "type": "string"
        }
      ]
    },
    "IssueStatus": {
      "enum": [
        "Open",
        "Confirmed",
        "Resolved",
        "Closed",
        "Reopened"
      ],
      "type": "string"
    },
    "MethodId": {
      "format": "uint32",
      "minimum": 0,
      "type": "integer"
    },
    "ResolutionKind": {
      "oneOf": [
        {
          "enum": [
            "Static",
            "Unresolved"
          ],
          "type": "string"
        },
        {
          "const": "PointsTo",
          "description": "Plano CPG Fase 2: dispatch resolvido via points-to (Andersen) — o\nreceiver é variável cujo pt-set singleton aponta a alloc de classe\nconhecida. Procedência distinta de `Static` de propósito (visível na\nview CallGraph e auditável no envelope).",
          "type": "string"
        },
        {
          "const": "TypeHierarchy",
          "description": "Plano symbol-table-types F2.3: dispatch resolvido via CHA — o receiver\ntem TIPO estático conhecido e a hierarquia de classes dá um único alvo\n(RTA-preciso ou tipo com implementação única). Cobre typed-params/locais\nque o points-to não vê. Cross-file (same-file precisa de F1.4).",
          "type": "string"
        },
        {
          "const": "NameFanout",
          "description": "**May-call por NOME** (2026-08-16): linguagem sem tipo estático no\nreceiver (Python/JS), nome de método com >1 candidato no projeto e\nnenhuma outra via resolveu. Cria aresta para TODOS os candidatos —\nover-aproximação deliberada, que é o que a análise de taint quer: se\nalgum alvo propaga, o fluxo existe.\n\nProcedência SEPARADA de `TypeHierarchy` de propósito: aquela tem tipo\nestático por trás e é evidência mais forte; misturar as duas corromperia\na métrica de resolução do `cpg-score`.",
          "type": "string"
        }
      ]
    },
    "Severity": {
      "enum": [
        "Blocker",
        "Critical",
        "Major",
        "Minor",
        "Info"
      ],
      "type": "string"
    },
    "SoftwareQuality": {
      "description": "Sprint 61 (S1): eixo de qualidade de software da taxonomia Clean Code\n(SonarQube 10.x+). Substitui o antigo `RuleType` como dimensão primária.",
      "enum": [
        "Maintainability",
        "Reliability",
        "Security"
      ],
      "type": "string"
    },
    "StatementId": {
      "format": "uint32",
      "minimum": 0,
      "type": "integer"
    },
    "SuspectReason": {
      "description": "Por que um finding ficou `Suspected` — pista para a triagem do agente.",
      "oneOf": [
        {
          "const": "sanitizer_cut",
          "description": "Um sanitizer (universal ou de categoria) cortou um fluxo que senão seria\nsource→sink. Vale revisar: o sanitizer pode não cobrir o caso real (bypass).",
          "type": "string"
        },
        {
          "const": "path_unclosed",
          "description": "**F1a.2** — sink alcançável a partir de source cujo caminho não fechou na\nprova estrita (StringBuilder/coleção/passthrough não modelado).",
          "type": "string"
        },
        {
          "const": "uncataloged_source",
          "description": "**F1a.2** — sink alcançável a partir de origem fora do catálogo de sources.",
          "type": "string"
        }
      ]
    },
    "TaintHop": {
      "description": "Um passo inter-procedural do caminho de taint (versão leve do\n`analysis::ddg_interproc::InterprocEdge` — só o que consumidores externos\nprecisam, mantendo `issues/` desacoplado de `analysis/`).",
      "properties": {
        "call_line": {
          "description": "Linha do call site no caller.",
          "format": "uint32",
          "minimum": 0,
          "type": "integer"
        },
        "from_var": {
          "type": "string"
        },
        "kind": {
          "description": "`\"ArgToParam\"` (arg→param) ou `\"ReturnToResult\"` (return→lhs).",
          "type": "string"
        },
        "to_var": {
          "type": "string"
        }
      },
      "required": [
        "call_line",
        "kind",
        "from_var",
        "to_var"
      ],
      "type": "object"
    },
    "TaintMeta": {
      "description": "**Metadados ESTRUTURADOS do taint** (fecha a dívida das entradas 110/121): os\ncampos que o dashboard/SARIF/consumidores externos precisam, emitidos como\ndados — não mais parseados das mensagens formatadas do `flow`\n(`\"sink: cursor.execute()\"`). Espelha o `TaintFinding` do motor. `None` para\nissues não-taint. Aditivo (skip quando None).",
      "properties": {
        "guarded": {
          "description": "**Fase 3.1** — sink control-dependente de um branch com validador da\ncategoria (CDG). Sinal de triagem; NÃO suprime o finding. Aditivo.",
          "type": "boolean"
        },
        "interproc_path": {
          "items": {
            "$ref": "#/definitions/TaintHop"
          },
          "type": "array"
        },
        "sanitizer_absence_score": {
          "description": "`sanitizer_absence_score` (B.6a): 1.0 = sem sanitizer; <1.0 = sanitizer de\ncategoria diferente no caminho (não protege esta categoria de sink).",
          "format": "double",
          "type": "number"
        },
        "sink_call": {
          "type": "string"
        },
        "sink_line": {
          "format": "uint32",
          "minimum": 0,
          "type": "integer"
        },
        "sink_proximity": {
          "description": "Nº de nós do caminho: 1 = intra-method direto; +1 por hop inter-procedural.",
          "format": "uint",
          "minimum": 0,
          "type": "integer"
        },
        "source_call": {
          "type": "string"
        },
        "source_line": {
          "format": "uint32",
          "minimum": 0,
          "type": "integer"
        },
        "var": {
          "description": "Variável tainted no sink (`<inline>` quando o arg é a própria call-source).",
          "type": "string"
        }
      },
      "required": [
        "source_call",
        "source_line",
        "sink_call",
        "sink_line",
        "var",
        "sink_proximity",
        "sanitizer_absence_score"
      ],
      "type": "object"
    },
    "TaintVerdict": {
      "description": "Veredito de duas camadas (plan-duas-camadas, F1a, 2026-07-11). `Confirmed`:\nfluxo source→sink provado — o que entra na conta de precisão e é assinável\npelo attest. `Suspected`: candidato de baixa confiança recuperado **sem tocar**\na precisão do `confirmed`; hoje emitido quando um sanitizer cortou um fluxo que\nsenão seria source→sink (o agente verifica se o sanitizer de fato neutraliza).\nAditivo (serde default `Confirmed`) — findings legados assumem provado.",
      "enum": [
        "confirmed",
        "suspected"
      ],
      "type": "string"
    }
  },
  "description": "Report of the SRC moment. `fort` is the composite modality (SAST, SCA, quality, secrets and IaC, risk correlation, CPG substrate), not the binary name; provenance travels in `tool`. See ADR-0002.",
  "properties": {
    "artifacts": {
      "properties": {
        "call_edges": {
          "items": {
            "properties": {
              "call_site_stmt": {
                "$ref": "#/definitions/StatementId"
              },
              "callee": {
                "anyOf": [
                  {
                    "$ref": "#/definitions/MethodId"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "caller": {
                "$ref": "#/definitions/MethodId"
              },
              "id": {
                "$ref": "#/definitions/CallSiteId"
              },
              "resolution": {
                "$ref": "#/definitions/ResolutionKind"
              }
            },
            "required": [
              "id",
              "caller",
              "resolution",
              "call_site_stmt"
            ],
            "type": "object"
          },
          "type": "array"
        },
        "cfg_blocks": {
          "items": {
            "description": "Uma linha do artefato `cfg_blocks`. `line_start`/`line_end` são 1-based,\nderivadas dos statements do bloco; 0 = bloco sintético sem statements\n(entry/exit/merge).",
            "properties": {
              "block": {
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "is_entry": {
                "type": "boolean"
              },
              "is_exit": {
                "type": "boolean"
              },
              "line_end": {
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "line_start": {
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "method": {
                "$ref": "#/definitions/MethodId"
              }
            },
            "required": [
              "method",
              "block",
              "line_start",
              "line_end",
              "is_entry",
              "is_exit"
            ],
            "type": "object"
          },
          "type": "array"
        },
        "cfg_edges": {
          "items": {
            "description": "Uma linha do artefato `cfg_edges`. `kind` ∈ {fallthrough, true, false,\nback, exception, break, continue} (ver `EdgeKind::label`).",
            "properties": {
              "kind": {
                "type": "string"
              },
              "method": {
                "$ref": "#/definitions/MethodId"
              },
              "source": {
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "target": {
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              }
            },
            "required": [
              "method",
              "source",
              "target",
              "kind"
            ],
            "type": "object"
          },
          "type": "array"
        },
        "cpg_nodes": {
          "items": {
            "description": "View leve sobre qualquer nó das arenas (`BasicBlock`, `SSAValue`, etc.) para\nserialização e queries cross-pass — sem unificar os structs reais.\n2026-07-18 (plano CPG Fase 0.3): ganhou `name`/`line`/`end_line` — o artefato\n`cpg_nodes` serializava só `{kind,id,file_id}`, inútil para consumo externo.\n`name` vazio = nó sem texto (absorve o antigo mapa paralelo `node_names`).",
            "properties": {
              "end_line": {
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "file_id": {
                "$ref": "#/definitions/FileId"
              },
              "id": {
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "kind": {
                "$ref": "#/definitions/CpgNodeKind"
              },
              "line": {
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "name": {
                "type": "string"
              }
            },
            "required": [
              "kind",
              "id",
              "file_id",
              "line",
              "end_line"
            ],
            "type": "object"
          },
          "type": "array"
        },
        "ddg_edges": {
          "items": {
            "description": "Edge do DDG: def `from_def` alcança use em `to_line`.",
            "properties": {
              "from_def": {
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "method_id": {
                "$ref": "#/definitions/MethodId"
              },
              "to_use_line": {
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "var": {
                "type": "string"
              }
            },
            "required": [
              "from_def",
              "to_use_line",
              "var",
              "method_id"
            ],
            "type": "object"
          },
          "type": "array"
        },
        "dependencies": {
          "items": {
            "description": "Artefato `dependencies` do envelope (plano CPG 1.3/DepsGraph): o inventário\ndo SCA que antes só existia na emissão de SBOM, agora com o sinal de\nalcançabilidade de import (mesma semântica de `tag_import_reachability` —\npositivo-só, ausência não prova inalcançável).",
            "properties": {
              "direct": {
                "description": "Posição na árvore de deps (W2.5/NTIA): `Some(true)` = declarada direta\nno manifest/root do lockfile, `Some(false)` = transitiva, `None` = o\nlockfile não codifica essa informação (formatos flat).",
                "type": [
                  "boolean",
                  "null"
                ]
              },
              "ecosystem": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "reachable": {
                "description": "Import do pacote aparece no código analisado (positivo-só).",
                "type": "boolean"
              },
              "version": {
                "type": "string"
              }
            },
            "required": [
              "ecosystem",
              "name",
              "version",
              "reachable"
            ],
            "type": "object"
          },
          "type": "array"
        },
        "diagnostics": {
          "items": {
            "properties": {
              "file": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "kind": {
                "$ref": "#/definitions/DiagnosticKind"
              },
              "level": {
                "$ref": "#/definitions/DiagnosticLevel"
              },
              "line": {
                "format": "uint32",
                "minimum": 0,
                "type": [
                  "integer",
                  "null"
                ]
              },
              "message": {
                "type": "string"
              }
            },
            "required": [
              "level",
              "kind",
              "message"
            ],
            "type": "object"
          },
          "type": "array"
        },
        "files": {
          "items": {
            "properties": {
              "id": {
                "$ref": "#/definitions/FileId"
              },
              "language": {
                "type": "string"
              },
              "ncloc": {
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "path": {
                "type": "string"
              },
              "sha256": {
                "type": "string"
              }
            },
            "required": [
              "id",
              "path",
              "language",
              "sha256",
              "ncloc"
            ],
            "type": "object"
          },
          "type": "array"
        },
        "heap_allocs": {
          "items": {
            "properties": {
              "id": {
                "default": 0,
                "description": "Sprint 11: id estável dentro do envelope para cross-referência via\n`AllocSite.heap_id`. Atribuído após sort no runtime.",
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "kind": {
                "$ref": "#/definitions/HeapAllocKind"
              },
              "line": {
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "method_id": {
                "$ref": "#/definitions/MethodId"
              },
              "type_hint": {
                "description": "Hint textual do tipo: `SomeClass`, `dict`, `list`, `string`, etc.",
                "type": "string"
              }
            },
            "required": [
              "method_id",
              "line",
              "kind",
              "type_hint"
            ],
            "type": "object"
          },
          "type": "array"
        },
        "hir": {
          "items": {
            "properties": {
              "kind": {
                "$ref": "#/definitions/HirStmtKind"
              },
              "line": {
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "method_id": {
                "$ref": "#/definitions/MethodId"
              }
            },
            "required": [
              "method_id",
              "line",
              "kind"
            ],
            "type": "object"
          },
          "type": "array"
        },
        "issues": {
          "items": {
            "properties": {
              "blame": {
                "anyOf": [
                  {
                    "$ref": "#/definitions/BlameInfo"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "**Refino A.4 — blame per-linha.** Autor que por último tocou a linha deste\nfinding (`--git`). Atribuição precisa por finding; alimenta a anomalia\nhistórico-autoral line-level no risk-scoring. `None` sem `--git` ou quando\no blame não pôde ser computado. Aditivo (skip quando None) — set\npós-pipeline, fora do cache (não bumpa `CACHED_ARTIFACTS_SCHEMA`)."
              },
              "code_snippet": {
                "type": "string"
              },
              "col_end": {
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "col_start": {
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "confidence": {
                "$ref": "#/definitions/Confidence"
              },
              "cwe": {
                "items": {
                  "format": "uint32",
                  "minimum": 0,
                  "type": "integer"
                },
                "type": "array"
              },
              "dataflow_signal": {
                "description": "**Refino Fase D — sinal dataflow consolidado** ∈ [0,1] para issues de\ntaint: combina `sanitizer_absence_score` (B.6a — 1.0 sem sanitizer, 0.5\nsanitizer de categoria errada) × proximidade `1/(1+hops)` (B.6c — fluxo\ndireto vale mais que distante). `None` para issues não-taint. Consumido\npelo risk-scoring (Fase D) p/ modular a dimensão `dataflow` — fecha o loop\n\"sinal emitido → sinal usado\". Aditivo (skip quando None).",
                "format": "double",
                "type": [
                  "number",
                  "null"
                ]
              },
              "effort_minutes": {
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "entrypoint": {
                "anyOf": [
                  {
                    "$ref": "#/definitions/Entrypoint"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "**PC-1.2** — rota HTTP do handler que o fluxo de taint atravessa (`analysis::entrypoints`). `None` fora de\nhandler roteado ou para issue não-taint."
              },
              "file": {
                "type": "string"
              },
              "fix": {
                "description": "**Sprint 66** (autofix): texto de substituição sugerido para a região\ndevolvida por [`Issue::fix_region`] (estilo `fix:` do OpenGrep).\n`None` = sem fix. Emitido em SARIF (`fixes[]`) e aplicável via\n`analyze --apply-fixes`.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "fix_span": {
                "description": "**S2-1 (fix de taint):** região `[start, end)` a substituir, quando NÃO\ncoincide com `[col_start, col_end)`. Existe porque `col_start` alimenta\n`vuln_hash`/`fuzzy_hash` (`infra/runtime.rs`): findings de taint são\nline-level (`col_start = 0`) e alterá-los quebraria baseline, supressão de\nFP e os vereditos do agente (F2, chaveados por hash). O fix precisa apontar\npara a **variável tainted** na linha do sink — daí uma região própria,\ninvisível ao hash. `None` ⇒ usa `[col_start, col_end)`.",
                "items": [
                  {
                    "format": "uint32",
                    "minimum": 0,
                    "type": "integer"
                  },
                  {
                    "format": "uint32",
                    "minimum": 0,
                    "type": "integer"
                  }
                ],
                "maxItems": 2,
                "minItems": 2,
                "type": [
                  "array",
                  "null"
                ]
              },
              "flow": {
                "description": "**codeFlows (degrau 2):** passos source→sink do taint que originou esta\nissue. Vazio para issues não-taint. Emitido no SARIF `codeFlows`.",
                "items": {
                  "$ref": "#/definitions/FlowStep"
                },
                "type": "array"
              },
              "fuzzy_hash": {
                "default": "",
                "description": "Hash secundário sem linha — fuzzy match para issues que se moveram.\nSprint 5 / Onda 3.4.",
                "type": "string"
              },
              "id": {
                "description": "Identidade DETERMINÍSTICA do issue no envelope: UUID v5 de `vuln_hash#ordinal` num\nnamespace fixo, atribuído pelo runtime depois da ordenação estável (ver\n[`Issue::deterministic_id`]). Até a esteira D-4 (2026-08-22) era `Uuid::new_v4()` —\naleatório por execução — e ninguém notou porque o gate de determinismo só comparava os\nartefatos de grafo; o fuzz diferencial (`benchctl determinism`) o pegou em 18/40\ncasos no primeiro uso. A chave estável de um finding entre scans é `vuln_hash`; o `id`\nidentifica o issue DENTRO do envelope e, agora, igual em duas execuções.",
                "type": "string"
              },
              "is_new": {
                "description": "**Fase A.2 — diff-aware.** `true` quando a linha deste finding está num\nhunk ADICIONADO/alterado desde o `base` (`analyze --diff <base>`). `false`\npor default (sem `--diff`, ou linha intacta). Findings em código novo são\nmais acionáveis (regressão introduzida agora). Aditivo (skip quando false).",
                "type": "boolean"
              },
              "kind": {
                "$ref": "#/definitions/IssueKind"
              },
              "line": {
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "message": {
                "type": "string"
              },
              "owasp": {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              "priority_score": {
                "default": 0.0,
                "description": "**Sprint 14**: score de priorização para o agente downstream.\nCalculado de severity × confidence × kind / effort_factor. Quanto maior,\nmaior a urgência. Issues no envelope são ordenadas por este campo\ndescendente.\nSprint 23: f64 (não f32) para deterministic bit-a-bit em JSON roundtrip.\nf32→JSON→f32 perde 1 ULP em alguns valores; f64 não.",
                "format": "double",
                "type": "number"
              },
              "resolution": {
                "anyOf": [
                  {
                    "$ref": "#/definitions/IssueResolution"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "rule_id": {
                "type": "string"
              },
              "secondary_qualities": {
                "description": "**Eixos SECUNDÁRIOS (ADR-0001 item 2, esteira D-6).** O eixo PRIMÁRIO continua sendo\n`kind.software_quality()` — é ele que decide tier (Free = Security), gates e scoring;\naqui ficam os sinais adicionais que o finding carrega sem partição dura (\"engines se\napoiam, emissões se separam\"): catch silencioso é Reliability + Security; complexidade é\nMaintainability + Reliability. Saem como `impacts[]` adicionais no SonarQube e como\ndado no envelope; **nunca** mudam em que gate o finding conta. Vazio = mono-eixo\n(ausente no JSON — o contrato não muda para quem não usa).",
                "items": {
                  "$ref": "#/definitions/SoftwareQuality"
                },
                "type": "array"
              },
              "severity": {
                "$ref": "#/definitions/Severity"
              },
              "status": {
                "$ref": "#/definitions/IssueStatus"
              },
              "tags": {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              "taint": {
                "anyOf": [
                  {
                    "$ref": "#/definitions/TaintMeta"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "**Metadados estruturados do taint** (source/sink call, var, proximidade,\ncaminho inter-procedural). Emitido como DADO — o front deixa de parsear as\nmensagens do `flow` p/ recuperá-los. `None` para issues não-taint."
              },
              "tier": {
                "allOf": [
                  {
                    "$ref": "#/definitions/FindingTier"
                  }
                ],
                "description": "**F1a** — camada do veredito (`confirmed`/`suspected`). Default `Confirmed`;\nserializado só quando `Suspected` (aditivo, não altera snapshots existentes)."
              },
              "vuln_hash": {
                "type": "string"
              }
            },
            "required": [
              "id",
              "rule_id",
              "kind",
              "severity",
              "confidence",
              "status",
              "message",
              "file",
              "line",
              "col_start",
              "col_end",
              "code_snippet",
              "cwe",
              "owasp",
              "tags",
              "effort_minutes",
              "vuln_hash"
            ],
            "type": "object"
          },
          "type": "array"
        },
        "method_metrics": {
          "items": {
            "properties": {
              "blocks": {
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "cognitive": {
                "default": 0,
                "description": "Complexidade COGNITIVA per-método (Q2, plan-vibe-quality): a mesma\ntravessia cognitiva de `analyze` (nesting-aware), restrita ao subtree do\nmétodo. Antes só havia agregado por-arquivo (`Metrics.cognitive_complexity`);\no advisory acionável precisa do valor por-método. 0 se a linguagem/range\nnão produzir corpo (mesma degradação silenciosa dos demais campos).",
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "cyclomatic": {
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "edges": {
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "end_line": {
                "default": 0,
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "file": {
                "default": "",
                "description": "Tier 5 — localização do método (p/ overlay de findings no callgraph e\nqualquer consumidor que precise mapear método→arquivo). `file` é\npreenchido pelo caller (per-file); `line`/`end_line` = range [1ª, última]\nlinha do corpo (1-based). `end_line` habilita overlay RANGE-LEVEL — só o\nmétodo que contém a linha do finding acende (não o arquivo inteiro).",
                "type": "string"
              },
              "line": {
                "default": 0,
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "method_id": {
                "$ref": "#/definitions/MethodId"
              },
              "method_name": {
                "type": "string"
              }
            },
            "required": [
              "method_id",
              "method_name",
              "cyclomatic",
              "blocks",
              "edges"
            ],
            "type": "object"
          },
          "type": "array"
        },
        "metrics": {
          "items": {
            "description": "Snapshot de métricas por escopo (file/namespace/project). Mock inicial: só\nsoma simples sem agregação hierárquica complexa.",
            "properties": {
              "bugs": {
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "classes": {
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "code_smells": {
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "cognitive_complexity": {
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "comment_lines": {
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "complexity": {
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "duplicated_blocks": {
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "duplicated_lines": {
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "duplicated_lines_density": {
                "format": "float",
                "type": "number"
              },
              "functions": {
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "ncloc": {
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "security_hotspots": {
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "statements": {
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "technical_debt_minutes": {
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "vulnerabilities": {
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              }
            },
            "required": [
              "ncloc",
              "comment_lines",
              "statements",
              "functions",
              "classes",
              "complexity",
              "cognitive_complexity",
              "duplicated_lines",
              "duplicated_blocks",
              "duplicated_lines_density",
              "vulnerabilities",
              "bugs",
              "code_smells",
              "security_hotspots",
              "technical_debt_minutes"
            ],
            "type": "object"
          },
          "type": "array"
        },
        "points_to": {
          "items": {
            "description": "Conjunto pt(var) para uma variável (potencialmente field) dentro de um método.",
            "properties": {
              "field": {
                "default": null,
                "description": "Sprint 6: campo opcional. `Some(\"f\")` ⇒ pt(var.f).",
                "type": [
                  "string",
                  "null"
                ]
              },
              "method_id": {
                "$ref": "#/definitions/MethodId"
              },
              "targets": {
                "items": {
                  "$ref": "#/definitions/AllocSite"
                },
                "type": "array"
              },
              "var": {
                "type": "string"
              }
            },
            "required": [
              "method_id",
              "var",
              "targets"
            ],
            "type": "object"
          },
          "type": "array"
        },
        "resolved_baseline_hashes": {
          "items": {
            "type": "string"
          },
          "type": "array"
        },
        "sccs": {
          "type": "array"
        },
        "ssa_phis": {
          "items": {
            "description": "Phi node em um bloco com múltiplos predecessores.",
            "properties": {
              "block_id": {
                "$ref": "#/definitions/BlockId"
              },
              "incoming": {
                "description": "Pares (predecessor, version_in_pred).",
                "items": {
                  "items": [
                    {
                      "$ref": "#/definitions/BlockId"
                    },
                    {
                      "format": "uint32",
                      "minimum": 0,
                      "type": "integer"
                    }
                  ],
                  "maxItems": 2,
                  "minItems": 2,
                  "type": "array"
                },
                "type": "array"
              },
              "method_id": {
                "$ref": "#/definitions/MethodId"
              },
              "result_version": {
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "var": {
                "type": "string"
              }
            },
            "required": [
              "method_id",
              "block_id",
              "var",
              "result_version",
              "incoming"
            ],
            "type": "object"
          },
          "type": "array"
        },
        "ssa_versions": {
          "items": {
            "description": "Versão SSA de uma variável (resultado do rename pass).",
            "properties": {
              "block_id": {
                "$ref": "#/definitions/BlockId"
              },
              "line": {
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "method_id": {
                "$ref": "#/definitions/MethodId"
              },
              "source": {
                "description": "Origem: `def` (assignment), `phi` (junção), `param` (entrada).",
                "type": "string"
              },
              "var": {
                "type": "string"
              },
              "version": {
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              }
            },
            "required": [
              "method_id",
              "block_id",
              "var",
              "version",
              "line",
              "source"
            ],
            "type": "object"
          },
          "type": "array"
        },
        "taint_findings": {
          "items": {
            "properties": {
              "context_chain": {
                "default": [],
                "description": "**Sprint 31** (k=1): call-chain `(caller, line)` que levou a este\nfinding. Para findings intra-method puros, fica vazio. Em\n`analyze_full_kn(k=1)` é populado a partir do `via_callsites`\ndeterminístico (depth-1, espelha `via_callsites`). Campo\nschema-aditivo — consumidores que não o usam podem ignorá-lo.",
                "items": {
                  "items": [
                    {
                      "format": "uint32",
                      "minimum": 0,
                      "type": "integer"
                    },
                    {
                      "format": "uint32",
                      "minimum": 0,
                      "type": "integer"
                    }
                  ],
                  "maxItems": 2,
                  "minItems": 2,
                  "type": "array"
                },
                "type": "array"
              },
              "guarded": {
                "description": "**Plano CPG Fase 3.1** — o sink é control-dependente (via CDG) de um\nbranch cuja condição chama um sanitizer/validador CATEGORIZADO que cobre\na categoria do sink (ex.: sink `command` guardado por `if is_safe(x)`).\nSinal de PRECISÃO **aditivo**: NÃO suprime o finding (o guard pode ter\nbypass — recall preservado), só marca \"há uma validação no caminho\" para\npriorização/triagem. Aditivo (serde default false).",
                "type": "boolean"
              },
              "interproc_path": {
                "default": [],
                "description": "**Fase 3.3-wire:** caminho inter-procedural **variable-level** preciso,\nreconstruído a partir do artefato `interproc_ddg_edges` (hops\n`ArgToParam` que injetaram taint na função do sink, ordem source→sink).\nVazio para findings intra-method. Aditivo (serde default) — eleva\n`via_callsites` (line-level) para o nível de variável/edge real.",
                "items": {
                  "$ref": "#/definitions/InterprocEdge"
                },
                "type": "array"
              },
              "method_id": {
                "$ref": "#/definitions/MethodId"
              },
              "method_name": {
                "type": "string"
              },
              "sanitizer_absence_score": {
                "default": 1.0,
                "description": "**B.6a (degrau 4) — sinal de sanitização contextual.** `1.0` quando\nNENHUM sanitizer tocou o caminho source→sink (risco pleno). `< 1.0`\n(hoje `0.5`) quando um sanitizer CATEGORIZADO esteve no caminho mas **não\ncobre a categoria deste sink** (ex.: `htmlEscape` num caminho SQL — o dev\ntentou sanitizar, mas no contexto errado: ainda é bug, porém de menor\nprioridade que um caminho cru). Caminhos com sanitizer que CASA a\ncategoria são suprimidos (não viram finding). Consumido pelo risk-scoring\n(Fase D) como `sanitizer_absence_score`. Aditivo (serde default 1.0).",
                "format": "double",
                "type": "number"
              },
              "sink_call": {
                "type": "string"
              },
              "sink_line": {
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "source_call": {
                "type": "string"
              },
              "source_line": {
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "suspect_reason": {
                "anyOf": [
                  {
                    "$ref": "#/definitions/SuspectReason"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "**F1a** — motivo, quando `verdict == Suspected`. `None` para confirmados."
              },
              "var": {
                "type": "string"
              },
              "verdict": {
                "allOf": [
                  {
                    "$ref": "#/definitions/TaintVerdict"
                  }
                ],
                "default": "confirmed",
                "description": "**F1a** — camada do veredito. `Confirmed` (default) para todo fluxo provado;\n`Suspected` para candidatos recuperados (ver `suspect_reason`). Aditivo."
              },
              "via_callsite": {
                "default": null,
                "description": "**Sprint 13** (k=1 lite): se este finding foi propagado via uma call\nespecífica que injetou taint num parâmetro, registra\n`(caller_method_id, callsite_line)`. `None` para findings intra-method\npuros. **Mantido para retro-compat** — campo \"primário\" é `via_callsites`.",
                "items": [
                  {
                    "format": "uint32",
                    "minimum": 0,
                    "type": "integer"
                  },
                  {
                    "format": "uint32",
                    "minimum": 0,
                    "type": "integer"
                  }
                ],
                "maxItems": 2,
                "minItems": 2,
                "type": [
                  "array",
                  "null"
                ]
              },
              "via_callsites": {
                "default": [],
                "description": "**Sprint 14** (k=1 contextual): lista completa de callsites que\ninjetaram taint no método deste finding. `via_callsite` é o último\ndessa lista (ou `None` se vazia). Permite ao agente downstream\nreconstruir todos os caminhos.",
                "items": {
                  "items": [
                    {
                      "format": "uint32",
                      "minimum": 0,
                      "type": "integer"
                    },
                    {
                      "format": "uint32",
                      "minimum": 0,
                      "type": "integer"
                    }
                  ],
                  "maxItems": 2,
                  "minItems": 2,
                  "type": "array"
                },
                "type": "array"
              }
            },
            "required": [
              "method_id",
              "method_name",
              "source_line",
              "source_call",
              "sink_line",
              "sink_call",
              "var"
            ],
            "type": "object"
          },
          "type": "array"
        }
      },
      "type": "object"
    },
    "repository": {
      "type": "object"
    },
    "run": {
      "type": "object"
    },
    "schema": {
      "const": "coda-fort/1"
    },
    "schema_version": {
      "type": "string"
    },
    "tool": {
      "type": "object"
    }
  },
  "required": [
    "schema",
    "schema_version",
    "tool",
    "repository",
    "run",
    "artifacts"
  ],
  "title": "coda-fort/1: source code analysis report",
  "type": "object",
  "version": "1.0"
}
