Diagnostic reports

Updated Jul 27, 2026

The JSON report is a versioned object:

{
  "schemaVersion": 1,
  "tool": { "name": "odw-lint", "version": "0.0.0" },
  "summary": {
    "files": 1,
    "filesSkipped": 0,
    "errors": 1,
    "warnings": 0,
    "infos": 0,
    "hints": 0
  },
  "diagnostics": [
    {
      "file": "workflows/example.js",
      "rule": "odw/meta-required",
      "severity": "error",
      "message": "Workflow source must export literal metadata.",
      "span": {
        "start": { "offset": 0, "line": 1, "column": 1 },
        "end": { "offset": 0, "line": 1, "column": 1 }
      },
      "docs": "docs/rules/meta-required.md",
      "suggestions": []
    }
  ],
  "ioErrors": []
}

Report consumers should treat schemaVersion, tool, summary, diagnostics, and ioErrors as stable top-level fields. summary.files counts readable files that were checked, and summary.filesSkipped counts input files that could not be read. ioErrors is always present and carries machine-readable read failures with file, reason, and message fields. Diagnostic spans point to the original source file, not to any normalized parser input. Offsets are zero-based UTF-8 byte offsets; lines and columns are one-based display positions. span.start is inclusive and span.end is exclusive. Point diagnostics may use a zero-length span where start and end are identical.

The default human text report is derived from the same diagnostic objects and summary counts as JSON output. Each diagnostic is printed as one file:line:column severity rule message line, followed by a blank line and a Found … severity summary such as Found 1 error. or Found 2 errors, 1 warning. Clean runs print nothing. This text is a human report, not a machine-parseable stream; use a machine-readable output format in Continuous Integration (CI) or editor integrations when callers need stable field names.