Skip to content

Suppressing findings

Wardline has three suppression layers, each for a different situation. All three key on a finding's full fingerprint — a stable 64-character hex hash — so a suppression survives across runs but is re-keyed if the finding's line moves (see the note at the bottom).

Layer Where it lives Authored by Use it when
Baseline .wardline/baseline.yaml wardline baseline create/update Adopting Wardline on an existing codebase: accept today's findings wholesale and gate only on new ones.
Waiver wardline.yaml (waivers:) a human One specific finding is a known false positive or accepted risk; you want a recorded reason and (optionally) an expiry.
Judged FP .wardline/judged.yaml the LLM judge (wardline judge --write) The opt-in judge ruled a finding a false positive and you accept that verdict.

When more than one layer matches a finding, precedence is waiver > judged > baseline — explicit human intent wins, and an LLM verdict wins over a silent baseline so its rationale is the visible reason. A scan summary reports the breakdown:

$ wardline scan .
scanned 2 file(s); 4 finding(s) — 1 suppressed (1 baseline / 0 waiver / 0 judged), 0 new -> findings.jsonl

Baseline

A baseline is a git-committable snapshot of findings you accept as-is. It is the fast on-ramp for an existing project: capture everything once, then let the --fail-on gate fire only on findings that appear after the snapshot.

wardline baseline [OPTIONS] COMMAND [ARGS]...

  Manage the finding baseline (.wardline/baseline.yaml).

Commands:
  create  Write a new baseline from current findings (refuses if one exists).
  update  Re-derive and overwrite the baseline from current findings.
wardline baseline create [OPTIONS] [PATH]

  Write a new baseline from current findings (refuses if one exists).

Options:
  --config PATH
  --help         Show this message and exit.

create writes .wardline/baseline.yaml and refuses to clobber an existing one; update re-derives and overwrites. Only DEFECT findings are baselined, and any finding with an active waiver is excluded (so its waiver expiry still resurfaces it later).

$ wardline baseline create .
baselined 1 finding(s) -> .wardline/baseline.yaml: 1 ERROR
$ wardline baseline create .
.wardline/baseline.yaml already exists; use `wardline baseline update` to overwrite.

The file carries rule_id / path / message per entry purely for human auditability in a git diff; only the fingerprint is loaded into the match set.

version: 1
entries:
- fingerprint: 7bd0099a6e87d1a7e5994d175da5dd5d5de422747b189e4223273ea8eaa9980d
  rule_id: PY-WL-101
  path: svc.py
  message: svc.leaky declares return trust INTEGRAL but actually returns EXTERNAL_RAW
    (less trusted) — untrusted data reaches a trusted producer

Commit .wardline/baseline.yaml. Re-run wardline baseline update whenever you intentionally accept a new batch of findings, then commit the diff.

Waivers

A waiver suppresses one finding by fingerprint, with a required reason and an optional ISO expiry. Waivers are hand-authored inline in wardline.yaml:

waivers:
  - fingerprint: 7bd0099a6e87d1a7e5994d175da5dd5d5de422747b189e4223273ea8eaa9980d
    reason: "validated downstream by the gateway; engine cannot see the guard"
    expires: 2026-12-31

Copy the fingerprint from a scan's JSONL output. The reason must be a non-empty string; a duplicate fingerprint or a non-ISO expires is a hard error.

$ wardline scan .
scanned 2 file(s); 4 finding(s) — 1 suppressed (0 baseline / 1 waiver / 0 judged), 0 new -> findings.jsonl

Expiry is inclusive: a waiver is active through its expires day and lapses only strictly after it (today > expires). When it lapses the finding resurfaces as active — an expiry is a built-in review reminder, not a permanent mute. Omit expires for a waiver that never lapses.

Reach for a waiver (not the baseline) when you have a specific, explained acceptance for one finding. The baseline is the bulk accept-everything tool; waivers are surgical and self-documenting.

Judged false positives

When you run the opt-in LLM triage judge with --write, its FALSE_POSITIVE verdicts (at or above the configured confidence floor) are appended to .wardline/judged.yaml. This is the same machine-vs-human split as the baseline: hand-authored waivers stay clean in wardline.yaml, while machine-judged FPs live in their own file with full provenance.

Each record carries the model's verbatim rationale — the audit primitive — plus model_id, confidence, recorded_at, and a policy_hash so a re-run under a changed prompt is a visible audit signal.

version: 1
findings:
- fingerprint: <64-hex>
  rule_id: PY-WL-101
  path: svc.py
  message: <finding message>
  verdict: FALSE_POSITIVE
  rationale: <verbatim model reasoning — the audit record>
  confidence: 0.9
  model_id: anthropic/claude-opus-4-8
  recorded_at: 2026-05-30T00:00:00+00:00
  policy_hash: sha256:<...>

Commit .wardline/judged.yaml like the baseline. A judged suppression is advisory — the rationale is recorded precisely so a human can audit it and revert by deleting the entry. See the LLM triage judge guide for how verdicts are produced and the --write confidence floor.

A note on line sensitivity

All three layers key on the full fingerprint, which includes the finding's start line (a deliberate strict-matching dial). A cosmetic edit that shifts a line — adding an import or a docstring — re-keys the finding: a previously baselined/waived/judged defect resurfaces as active. After a refactor that moves lines, regenerate the baseline (wardline baseline update) and re-copy any affected waiver fingerprints.