Ctrl K
ComunidadGuías…

src/severities.js

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
// SPDX-License-Identifier: GPL-3.0-or-later
/**
 * The single source of truth for severity semantics: the fixed set of levels,
 * their display labels, the numeric weights used for dedupe tie-breaking, and
 * the health-score penalties. Every module that must reason about severity
 * (dedupe, scoring, counts, rendering) imports from here instead of hardcoding
 * its own copy.
 */
export const SEV_ORDER = ["high", "medium", "info"];

export const SEV_LABEL = {
  high: "🔴 HIGH",
  medium: "🟡 MEDIUM",
  info: "⚪ INFO",
};

/** Numeric weight per severity — higher wins on a dedupe conflict. */
export const SEV_WEIGHT = { high: 3, medium: 2, info: 1 };

/** Health-score penalty per finding of this severity (info is free). */
export const SEV_PENALTY = { high: 15, medium: 8, info: 0 };

/**
 * Escalation on top of SEV_PENALTY: problems compound, and the score should
 * say so. The n-th finding of a tier (n counted within that tier) costs
 * SEV_PENALTY + SEV_ESCALATION × max(0, n − (SEV_ESCALATE_FROM − 1)).
 *   high:   each additional critical beyond the first costs +5 more
 *           (15, 20, 25, 30 …) — four failed units must not land anywhere
 *           near a pile of annoyances.
 *   medium: the first three stay at the flat 8; from the fourth on each one
 *           costs +1 more (a growing pile of "minor" issues is itself a
 *             problem).
 * Without this, 4 high (40) and 7 medium (44) scored almost identically,
 * which read as "critical ≈ cosmetic".
 */
export const SEV_ESCALATION = { high: 5, medium: 1, info: 0 };

/** The finding ordinal within its tier where SEV_ESCALATION kicks in. */
export const SEV_ESCALATE_FROM = { high: 2, medium: 4, info: Number.POSITIVE_INFINITY };

/** [{ severity, count }] for every severity, in canonical order. */
export function countBySeverity(findings) {
  return SEV_ORDER.map((s) => ({ severity: s, count: findings.filter((f) => f.severity === s).length }));
}

/**
 * Urgency, which is not severity. Severity says how bad a finding is; urgency
 * says whether a human has to act on it now. A degraded array that has run out
 * of redundancy and a full disk are "now"; a single corrected ECC error and a
 * pile of pending updates are not, however alarming their wording looks.
 * Grading everything by "how far from normal" is how a report teaches people
 * to stop reading it.
 *
 * Membership is an explicit list on purpose. Deriving it from
 * `severity === "high"` would let every future high finding claim a deadline
 * nobody reviewed it for, and would let an unknown (plugin) code into the
 * callout. A code joins this list when its grading is reviewed, the same way
 * the rest of the rubric was. A deadline is not urgency: an expiring
 * certificate or a disk filling up have a date, not a slope, so they are a
 * watch item with a date rather than a "drop what you are doing".
 */
const NOW_CODES = new Set([
  "disk/full", // nothing can be written; services start failing
  "inodes/full", // same failure mode, different counter
  "raid/degraded", // one more disk and the data is gone
  "smart/failing", // the drive is saying it is about to die
  "fs/io-errors", // the filesystem is losing writes
  "fs/btrfs-errors",
  "fs/readonly-remount",
  "hardware/mce", // uncorrected hardware fault
  "hardware/ecc", // only at high: an uncorrected error is data loss
]);

/** "now" | "watch" | "fyi" for one finding. Unknown codes are never "now". */
export function urgencyOf(finding) {
  if (!finding || finding.severity === "info") return "fyi";
  if (NOW_CODES.has(finding.code) && finding.severity === "high") return "now";
  return "watch";
}

/** Every finding that needs a human now, in report order. */
export function needsYouNow(findings) {
  return (findings || []).filter((f) => urgencyOf(f) === "now");
}