paranoid_
Report

The JSON report, its schema, and SARIF

paranoid writes one result in four formats: text for a terminal, md for a PR comment or job summary, json for scripting, and sarif for code scanning tools.

Formats

FormatUse
text (default)Reading in a terminal. Uses color on a real terminal and respects NO_COLOR.
mdPR comments, job summaries, anywhere Markdown renders.
jsonScripting and storage. Byte-identical across reruns of the same input.
sarifGitHub code scanning and other SARIF-reading tools.

Select a format with --format on verify. All four renderers read the same finding.Result, so a run produces the same findings in every format.

JSON report, schema version 9

The JSON report follows a versioned schema, checked in at docs/schema/report-v1.schema.json. A test in the repository checks every rendered report against this file. The schema allows no undeclared fields (additionalProperties: false everywhere). The current version is 9. Whenever a change adds a field, a category, or a report section, this number increases. A field does not change shape within a version.

Top-level fieldTypeMeaning
schema_versionintegerAlways 9 today
scoreinteger 0-100The trust score
verdict"pass" | "warn" | "fail"Derived from the score
findingsarray of findingEverything that fired, minus baseline and policy suppressions
checks_runarray of stringNames of checks that actually ran
checks_skippedarray of {name, reason}Checks that could not run, and why
coverageobjectTotal test coverage, best-effort; see Sandbox & coverage
suppressedobject {count, entries}Findings a baseline entry or a policy [[disable]] entry matched, see Baseline & suppressions and Team policy file
proofsobject, optionalFormal-verification summary (schema version 9). Present only when the formal-verification check ran; absent otherwise. Never changes the score. See the Proofs table below
metaobjectrepo, base, head, tool version, generated_at

Each item in findings:

FieldType
rule_idstring, e.g. "TI001"
severity"info" | "low" | "medium" | "high" | "critical"
category"test-integrity" | "dependencies" | "sandbox" | "claims" | "error-handling" | "api-surface" | "safety" | "formal-verification"
filestring
lineinteger, 0 when unknown
evidencestring, truncated to about 200 characters
messagestring
suggestionstring

Findings are sorted by rule ID, then file, then line, so JSON output is byte-identical across reruns. Nil slices render as empty arrays, never null.

Each item in suppressed.entries:

FieldType
rule_idstring
severity"info" | "low" | "medium" | "high" | "critical"
filestring
lineinteger, 0 when unknown
messagestring
reasonstring, empty for a bulk-generated baseline entry
expiresstring, empty when the entry never expires
expiredboolean
source"baseline" | "policy" (schema version 7)

Each field of proofs (when present):

FieldType
files_changedarray of string, the changed proof files, sorted
holesinteger, proof holes at head in the changed proof files
escapesinteger, axioms and escapes at head in the changed proof files
lockedobject {total, ok, drifted, missing}, the locked specs at head
recheckobject {ran, reason, build_ok, audited, disallowed_axioms, no_answer}; reason is left out when empty; the counts are 0 when the re-check did not run
{
  "schema_version": 9,
  "score": 40,
  "verdict": "fail",
  "findings": [
    {
      "rule_id": "DEP001",
      "severity": "critical",
      "category": "dependencies",
      "file": "requirements.txt",
      "line": 2,
      "evidence": "requests-toolkit-pro",
      "message": "The new dependency requests-toolkit-pro does not exist in PyPI.",
      "suggestion": "Remove the dependency or fix the typo."
    }
  ],
  "checks_run": ["test-integrity", "dependencies", "sandbox"],
  "checks_skipped": [{"name": "claims", "reason": "no --claims, --session, or commit messages given"}],
  "coverage": {"available": false, "reason": "no coverage tool detected", "head_percent": 0, "has_base": false, "base_percent": 0, "delta_percent": 0},
  "suppressed": {"count": 1, "entries": [
    {"rule_id": "TI012", "severity": "medium", "file": "flaky_test.go", "line": 12,
     "message": "A retry was added to an existing test.", "reason": "flaky under load, tracked in issue #142",
     "expires": "2026-12-01", "expired": false, "source": "policy"}
  ]},
  "proofs": {"files_changed": ["Proofs/Sort.lean"], "holes": 1, "escapes": 0,
    "locked": {"total": 2, "ok": 1, "drifted": 1, "missing": 0},
    "recheck": {"ran": false, "reason": "not requested", "build_ok": false, "audited": 0, "disallowed_axioms": 0, "no_answer": 0}},
  "meta": {"repo": "/home/you/project", "base": "7db5cfb", "head": "worktree", "version": "v0.1.0", "generated_at": "2026-08-26T12:00:00Z"}
}

SARIF (--format sarif)

SARIF 2.1.0, for upload to GitHub code scanning or any other SARIF reader. One run, one tool driver named paranoid, one reportingDescriptor per registered rule (id, short and full description, a helpUri pointing at that rule's anchor in docs/rules.md on the main branch), and one result per finding.

paranoid severitySARIF level
critical, higherror
mediumwarning
low, infonote

The original severity is preserved in properties.severity on the result even after it is mapped to a SARIF level. Score, verdict, checks run, checks skipped, and the suppressed block are all written to runs[0].properties, because SARIF has no matching concept for paranoid's report shape. The proofs block is written there too, only when the formal-verification check ran. Coverage is not included in SARIF properties.

SARIF is versioned by the SARIF spec itself, not by schema_version: a JSON report schema change does not change the SARIF shape, and the reverse also holds. See Exit codes & CI for the upload-sarif workflow example.

fix-prompt JSON schema

paranoid fix-prompt --format json emits a separate document with its own versioned schema at docs/schema/fix-prompt-v1.schema.json, currently version 1, independent of the verify report's schema version. See fix-prompt.

{
  "schema_version": 1,
  "instructions": [
    {
      "rule_id": "TI001",
      "severity": "critical",
      "file": "internal/calc/calc_test.go",
      "line": 0,
      "instruction": "Restore the deleted test file at internal/calc/calc_test.go; do not delete a test to make the suite pass."
    }
  ],
  "notes": []
}

History envelope, version 1

verify --save writes each report to .paranoid/history/, wrapped in a small envelope with its own history_version (currently 1), independent of both the verify schema and the fix-prompt schema: {history_version, report}, where report is the exact same object --format json would print for that run. paranoid history --format json emits a third, derived shape, a per-run series for scripting, which also has its own history_version. See History for both shapes, an example of each, and how the HTML trend view is built.

Both schema files are checked in and tested. A rendered report that does not match its schema fails the build. If your own tooling parses these formats, pin a schema version and treat an unexpected schema_version as an error, as fix-prompt --report does.