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.