pathlint 0.0.24

Lint the PATH environment variable against declarative ordering rules.
Documentation
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://raw.githubusercontent.com/ShortArrow/pathlint/main/schemas/check.schema.json",
  "title": "CheckOutcomeView",
  "description": "JSON shape of one element in the top-level array emitted by `pathlint check --json`. The discriminator is the `kind` field. See PRD §17 for the wire-shape history.",
  "type": "object",
  "required": [
    "command",
    "kind",
    "matched_sources",
    "severity"
  ],
  "properties": {
    "avoid": {
      "description": "`avoid` set copied from the source `[[expect]]` rule. Same optional-when-empty story as `prefer`.",
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "command": {
      "type": "string"
    },
    "diagnosis": {
      "anyOf": [
        {
          "$ref": "#/definitions/Diagnosis"
        },
        {
          "type": "null"
        }
      ]
    },
    "kind": {
      "description": "Outcome kind discriminator: `\"ok\"`, `\"ng_wrong_source\"`, `\"ng_unknown_source\"`, `\"ng_not_found\"`, `\"ng_not_executable\"`, `\"skip\"`, `\"not_applicable\"`, or `\"config_error\"`. Always a flat string (no nested object) since 0.0.17.",
      "allOf": [
        {
          "$ref": "#/definitions/Status"
        }
      ]
    },
    "matched_sources": {
      "description": "Sources matched against the resolved path. Always emitted — even an empty array — so consumers do not have to special-case \"field present\" vs \"field absent\". 0.0.17 hoisted this from schema-required-with-skip-serializing into a stable always-emit shape so the schema and the wire form match.",
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "prefer": {
      "description": "`prefer` set copied from the source `[[expect]]` rule. Skipped when empty so consumers reading typical Ok / Skip outcomes don't have to dig past the empty array. Schema marks the field optional via `#[serde(default)]`.",
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "reason": {
      "description": "Human-readable reason. Populated for `kind = \"ng_not_executable\"` (R2 shape check rejection) and `kind = \"config_error\"` (e.g. undefined source name). Absent otherwise. 0.0.17 split this off from the kind discriminator so consumers can branch on `kind` as a string and read `reason` separately.",
      "type": [
        "string",
        "null"
      ]
    },
    "resolved": {
      "type": [
        "string",
        "null"
      ]
    },
    "severity": {
      "description": "Per-rule severity copied from the Outcome. Always emitted (even for `error`, the default) so a downstream consumer gating on severity does not need a fallback for the absent case.",
      "allOf": [
        {
          "$ref": "#/definitions/Severity"
        }
      ]
    }
  },
  "definitions": {
    "Diagnosis": {
      "description": "Pure-data view of *why* an outcome failed. Derived from `Outcome` by `diagnose`; kept separate so the presentation layer renders strings from a structured value rather than from raw `Outcome` fields. `serde::Serialize` so the same value can drive `check --json`.\n\nVariants name the failure mode; the fields are the load-bearing facts callers need: which sources were missed (`prefer_missed`), which `avoid` names were hit (`avoid_hits`), the reason the shape check rejected the file, etc. The struct does *not* carry `command` / `resolved` — those live on `Outcome` and the caller pairs them up.",
      "oneOf": [
        {
          "description": "Resolved path matched some sources, but none of the `prefer` set — or it matched a source listed under `avoid`. `avoid_hits` is the intersection of `matched ∩ avoid`; non-empty means the rule explicitly forbids this source. `prefer_missed` is `prefer` itself (every name the user hoped for); rendering decides whether to show it.",
          "type": "object",
          "required": [
            "avoid_hits",
            "kind",
            "matched",
            "prefer_missed"
          ],
          "properties": {
            "avoid_hits": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "kind": {
              "type": "string",
              "enum": [
                "wrong_source"
              ]
            },
            "matched": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "prefer_missed": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          }
        },
        {
          "description": "Path lies outside every defined `[source.<name>]`. No source name matched at all.",
          "type": "object",
          "required": [
            "kind",
            "prefer"
          ],
          "properties": {
            "kind": {
              "type": "string",
              "enum": [
                "unknown_source"
              ]
            },
            "prefer": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          }
        },
        {
          "description": "Command was not on PATH, and the rule was not optional.",
          "type": "object",
          "required": [
            "kind",
            "prefer"
          ],
          "properties": {
            "kind": {
              "type": "string",
              "enum": [
                "not_found"
              ]
            },
            "prefer": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          }
        },
        {
          "description": "`kind = \"executable\"` shape check rejected the resolved file. `reason` is the short human-readable cause from `lint` (`\"is a directory\"`, `\"broken symlink\"`, …).",
          "type": "object",
          "required": [
            "kind",
            "matched",
            "reason"
          ],
          "properties": {
            "kind": {
              "type": "string",
              "enum": [
                "not_executable"
              ]
            },
            "matched": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "reason": {
              "type": "string"
            }
          }
        },
        {
          "description": "`[[expect]]` referenced a source name that is not defined.",
          "type": "object",
          "required": [
            "kind",
            "message"
          ],
          "properties": {
            "kind": {
              "type": "string",
              "enum": [
                "config"
              ]
            },
            "message": {
              "type": "string"
            }
          }
        }
      ]
    },
    "Severity": {
      "description": "Per-rule severity for `[[expect]]`. Defaults to `Error` so 0.0.x rules behave exactly as before.",
      "oneOf": [
        {
          "description": "NG escalates to exit 1. Default.",
          "type": "string",
          "enum": [
            "error"
          ]
        },
        {
          "description": "NG is reported but does not change the exit code.",
          "type": "string",
          "enum": [
            "warn"
          ]
        }
      ]
    },
    "Status": {
      "description": "Outcome status discriminator. Unit-only enum, serialised as a snake_case string under the `kind` field of `pathlint check --json`. Human-readable detail (when present) rides on `Outcome::reason`. See PRD §17 for the wire-shape history.",
      "oneOf": [
        {
          "type": "string",
          "enum": [
            "ok",
            "ng_wrong_source",
            "ng_unknown_source",
            "ng_not_found",
            "skip",
            "not_applicable",
            "config_error"
          ]
        },
        {
          "description": "R2 — resolved path failed `kind` shape check (directory, broken symlink, missing exec bit, etc.). The human-readable reason rides on `Outcome::reason`.",
          "type": "string",
          "enum": [
            "ng_not_executable"
          ]
        }
      ]
    }
  }
}