docs/severity.md
Rama main · réplica de lectura del repositorio original.
# Severity rubric
Every finding's severity is decided against this rubric — never per-case
intuition. The registry test (`tests/codes-registry.test.js`) pins every
built-in code to its allowed severity set; a change here must go together
with a manifest update and a CHANGELOG entry.
## The three levels
| Level | Question it answers | Typical findings |
|---|---|---|
| **High** | Is data loss, security exposure, or an unbootable/unusable system imminent? | `disk/full`, `fs/readonly-remount`, `smart/failing`, `ssh/root-password`, `crash/panic`, `hardware/mce` |
| **Medium** | Is the system actively degraded, or is a latent risk parked that becomes a real problem at the next incident? | `services/failed`, `memory/low`, `backup/none`-adjacent, `thermal/warm`, `updates/pending` (many), `gpu/nvidia-missing` |
| **Info** | Hygiene, context, or confirmation that a subsystem looks healthy | `zram/ok`, `wayland/x11`, `luks/encrypted`, `secureboot/enabled` |
Decision rules:
1. **Data loss / corruption risk → high.** Read-only remounts, SMART failing,
full disks, filesystem errors.
2. **Security exposure → high** when a service is *directly reachable*
(root SSH login allowed, password auth on root). **Medium** when exposure
requires a hostile network position.
3. **Degradation without data risk → medium**, even when very annoying
(software rendering, one process eating 40% of RAM). "Slow" is medium;
"dying" or "unbootable" is high.
4. **Latent risk → medium**: timers that never run, backups missing, pending
reboots, firmware updates waiting.
5. **Escalation by magnitude is allowed within one code** (e.g. `memory/low`
is high under 15% available, medium under 25%) but must stay inside the
code's pinned severity set in the registry.
6. **Healthy-state findings are always info** and only emitted when the check
actually saw the underlying data (enforced by `tests/contract.test.js`).
## Urgency is a separate axis
Severity answers "how bad is this". Urgency answers "does a human have to act
on it now". They usually agree, and where they do not, grading by severity alone
is how a report teaches people to stop reading it: a scheduled scrub, a single
corrected ECC error and a disk with hours of writes left all look alarming in
raw output, and two of them need nothing from you yet.
Every finding therefore carries a derived urgency — `now`, `watch` or `fyi`:
| Urgency | Meaning | How it is decided |
|---|---|---|
| **now** | A human should act today | The code is on the reviewed list in `src/severities.js` **and** its severity is high |
| **watch** | Worth knowing, worth a look soon | Everything else above `info` |
| **fyi** | Context | `info` |
Two rules keep it honest:
1. **Membership is a reviewed list, not `severity === "high"`.** A code joins
after its grading is reviewed, so a future high finding cannot claim a
deadline nobody agreed to, and a plugin's own code can never be `now`.
2. **A deadline is not urgency.** An expiring certificate or a disk filling up
has a date, not a slope: it is a `watch` item carrying its date, not
something to drop today for. `now` means the cost of waiting rises sharply
right now, usually because the remaining margin is gone.
3. **Urgency never changes the score.** The score is the run-over-run memory
(trend, NEW/FIXED); urgency only decides what gets called out. The report
lists the whole `now` set: a machine can have a full disk and a degraded
array at once, and one "start here" line used to hide the second.
## Documented exceptions
- `security/no-firewall` stays **info**: most desktop installs sit behind a
NAT router and ship with firewalld off; penalizing the default install in
the score would train users to ignore the score. The finding still explains
the public-Wi-Fi risk. Revisit if a fleet/server profile ever diverges.
## Health-score weights
Severity feeds the score via `src/severities.js`: base penalties 15/8/0 with
within-tier escalation (`SEV_ESCALATION`). Changing a finding's severity
changes scores on user machines — treat it as a breaking-ish change:
CHANGELOG entry required, history keeps working (scores are recomputed per
run, never stored as deltas).
## Changing a code or its severity
1. Update the check and the manifest in `tests/codes-registry.test.js`.
2. Add a CHANGELOG entry under **Changed** (codes are identity: scripts and
history diff by them).
3. Never rename a code without a migration story — old history entries keep
the old code and will read as fixed+new once.