src/schema.js
Rama main · réplica de lectura del repositorio original.
// SPDX-License-Identifier: GPL-3.0-or-later
/**
* JSON Schema (draft-07) for `linux-doctor --json` output, schemaVersion 1.
* `linux-doctor --schema` prints this document, so fleet servers, monitoring
* integrations, and CI pipelines can validate reports before consuming them.
*
* Versioning policy: ADDITIVE changes — new optional properties, never a
* new requirement and never a narrowed type — do NOT bump schemaVersion.
* Consumers must ignore unknown properties; producers must keep every
* emitted top-level key documented here (enforced by
* tests/output-drift.test.js). Incompatible shape changes bump the version
* and update this document in the same change.
*/
export const reportSchema = {
$schema: "http://json-schema.org/draft-07/schema#",
title: "linux-doctor report",
description: "Output of `linux-doctor --json` (schemaVersion 1).",
type: "object",
required: ["schemaVersion", "tool", "version", "generatedAt", "findings"],
properties: {
schemaVersion: { const: 1 },
tool: { const: "linux-doctor" },
version: { type: "string", description: "linux-doctor package version" },
generatedAt: { type: "string", format: "date-time" },
durationMs: { type: "number", minimum: 0 },
durations: {
type: "object",
description: "per-check wall-clock milliseconds; present only with --profile",
additionalProperties: { type: "number", minimum: 0 },
},
newCount: { type: "integer", minimum: 0 },
fixedCount: { type: "integer", minimum: 0 },
unchanged: { type: "integer", minimum: 0, description: "top-level mirror of diffSinceLast.unchanged (current findings also present in the previous run)" },
ignoredCount: { type: "integer", minimum: 0, description: "findings hidden by ignore patterns this run" },
checksRun: { type: "integer", minimum: 0, description: "how many checks actually ran" },
checksSkipped: { type: "integer", minimum: 0, description: "checks skipped by appliesTo gating (full runs only)" },
score: { type: "integer", minimum: 0, maximum: 100 },
scoreBreakdown: {
type: "array",
description: "per-finding penalty behind the health score, in finding order; info findings are listed with penalty 0. `100 − Σpenalty === score` holds by construction.",
items: {
type: "object",
required: ["severity", "penalty"],
properties: {
code: { type: ["string", "null"], description: "stable finding code, or null when the source carried none" },
severity: { enum: ["high", "medium", "info"] },
title: { type: ["string", "null"] },
penalty: { type: "integer", minimum: 0 },
},
},
},
scoreDelta: { type: ["integer", "null"], description: "score minus the previous run's score; null when there is no history or history is disabled (can be negative)" },
previousScore: { type: ["integer", "null"], minimum: 0, description: "the most recent stored run's score, or null" },
lastRunAt: { type: ["string", "null"], format: "date-time", description: "when the previous recorded run happened, or null" },
cleanStreak: { type: "integer", minimum: 0, description: "consecutive trailing clean runs including this one; 0 when this run has high/medium findings or history is off" },
checkErrors: {
type: "array",
description: "checks that threw while running; the run continued without them",
items: {
type: "object",
required: ["check", "error"],
properties: {
check: { type: "string" },
error: { type: "string" },
},
},
},
counts: {
type: "object",
properties: {
high: { type: "integer", minimum: 0 },
medium: { type: "integer", minimum: 0 },
info: { type: "integer", minimum: 0 },
},
},
diffSinceLast: {
type: "object",
properties: {
added: { type: "array", items: { $ref: "#/definitions/briefFinding" } },
fixed: { type: "array", items: { $ref: "#/definitions/briefFinding" } },
unchanged: { type: "integer", minimum: 0, description: "current findings whose key also appeared in the previous run" },
firstRun: { type: "boolean", description: "true when there was no previous run to compare against, so added/fixed/unchanged are all zero" },
},
},
historyDisabled: {
type: "boolean",
description: "true when --no-history / LINUX_DOCTOR_NO_HISTORY suppressed history read+write this run",
},
changeMessage: {
type: ["string", "null"],
description: "human one-liner summarizing the change since the last run, or null when nothing changed",
},
nextAction: {
type: ["object", "null"],
description: "the ▶ START HERE pick as data — highest-severity finding that comes with a fix; the dashboard renders this instead of recomputing. null when nothing has a fix.",
required: ["severity", "title"],
properties: {
code: { type: ["string", "null"] },
severity: { enum: ["high", "medium", "info"] },
title: { type: "string" },
fix: { type: ["string", "null"] },
},
},
skippedChecks: {
type: "array",
description: "checks that were intentionally skipped (not failed) with a reason — e.g. not applicable on an immutable system",
items: {
type: "object",
required: ["id", "title", "reason"],
properties: {
id: { type: "string" },
title: { type: "string" },
reason: { type: "string" },
},
},
},
checksAtomicSkipped: {
type: "integer",
minimum: 0,
description: "checks skipped because they do not apply to an immutable/atomic system",
},
system: {
type: "object",
properties: {
distro: { type: "string" },
family: { type: "string" },
kind: { enum: ["desktop", "laptop", "server"] },
kernel: { type: "string" },
cores: { type: ["integer", "string"], description: "CPU core count (string when nproc is unavailable)" },
uptime: { type: "string" },
arch: { type: ["string", "null"], description: "machine architecture from uname -m" },
hostname: { type: ["string", "null"], description: "system hostname (local-only detail, also surfaced in alerts)" },
cpuModel: { type: ["string", "null"], description: "CPU model name from /proc/cpuinfo" },
memTotalBytes: { type: ["integer", "null"], description: "total RAM in bytes from /proc/meminfo" },
desktop: { type: ["string", "null"], description: "desktop environment from XDG_CURRENT_DESKTOP/DESKTOP_SESSION" },
sessionType: { type: ["string", "null"], description: "session type from XDG_SESSION_TYPE (wayland/x11/tty…)" },
immutable: { type: "boolean" },
imageBased: { type: "boolean" },
hasSystemd: { type: "boolean", description: "true when the running init is systemd (/run/systemd/system exists); some fix commands are systemd-only and are withheld when false" },
atomicVariant: { type: ["string", "null"], description: "specific atomic variant (bazzite, silverblue, ...) or null" },
osRelease: {
type: "object",
description: "parsed /etc/os-release key/value pairs, as emitted by the runtime",
additionalProperties: { type: "string" },
},
atomic: {
type: "object",
description: "structured immutable/atomic profile so consumers adapt once",
properties: {
immutable: { type: "boolean" },
imageBased: { type: "boolean" },
bootc: { type: "boolean" },
variant: { type: ["string", "null"] },
pkg: { type: "string" },
},
},
},
},
findings: { type: "array", items: { $ref: "#/definitions/finding" } },
},
definitions: {
finding: {
type: "object",
required: ["id", "check", "code", "severity", "title"],
properties: {
id: { type: "integer", minimum: 1 },
check: { type: "string", description: "which check produced this finding" },
code: { type: "string", description: "stable machine key for scripting and ignore rules" },
dedupeKey: { type: "string", description: "root cause shared with another check's finding" },
severity: { enum: ["high", "medium", "info"] },
title: { type: "string" },
detail: { type: ["string", "null"] },
evidence: { type: ["string", "null"] },
fix: { type: ["string", "null"] },
confidence: { enum: ["high", "medium", "low"] },
isNew: { type: "boolean" },
},
},
briefFinding: {
type: "object",
required: ["severity", "title"],
properties: {
severity: { enum: ["high", "medium", "info"] },
title: { type: "string" },
code: { type: "string", description: "stable identity used for new/fixed change detection" },
},
},
},
};