Finding identity
Each finding gets a stable fingerprint built from what the finding is (rule, file, and the specific step/action involved) — deliberately not its line number, so edits that shift a finding down a file don’t change its identity. The fingerprint feeds a per-repo ledger recording when each finding was first seen, last seen, and when it stopped appearing (resolved). Two things surface from the ledger:- NEW badges — findings first seen by the latest scan are marked
NEWon the repository page, the latest-scan summary shows an “N new” chip, and the dashboard’s Total findings card notes how many findings are new across your latest scans. - Resolution tracking — a finding that disappears from a scan is marked resolved; if it comes back later it is not counted as new (the ledger remembers it).
Triage
Work the queue on the Triage page, or expand any finding on the repository page. Five states:
Triage is keyed to the fingerprint, so it survives re-scans — a dismissed
finding stays dismissed no matter how many times the repo is scanned (manually
or via monitoring).
Accepted risks expire
Accepting a risk and snoozing a finding are claims about time, so both require a date. Pipefort will not record either without one: an acceptance with no end is a permanent dismissal that nobody wrote down as one. Accept risk opens a sheet that asks for two things — a reason, and how long. The reason is mandatory, because it is the only record of the decision the product keeps: there is no separate audit log, and the text you write is what the next person to open the finding reads. A nightly sweep returns lapsed decisions to the queue as untriaged and recomputes the repository’s posture counts, so a risk coming back is a risk the dashboard admits to again. Until that sweep runs, a decision past its date is shown aslapsed rather than as one that still holds.
Acceptances made before this feature existed have no date. They are shown
as
accepted · no expiry and never return on their own — they behave
differently from every acceptance made since, so the queue says so out loud
rather than letting them pass for time-boxed ones. Reopen and re-accept one to
give it an end date.A finding remembers its fix PR
When Pipefort opens an auto-fix pull request, it records the link against every finding that PR addresses — the rule’s occurrences in the file it rewrote. Those findings move out of Untriaged and into Fix proposed, carrying aPR #244 badge, so nobody picks one up and opens a second pull request for the
same change.
This is not a triage status. The finding stays open and stays in your
posture counts, because the risk is live until the fix lands — what closes it
is the next scan not seeing it.
The drawer also says what became of that pull request, from two sources with
different guarantees:
- An outcome — merged, or closed without merging — arrives on a pull-request webhook and is final.
- An observation — a merge conflict, a failing check, a review nobody has given — comes from the daily poll behind the fix tracker, and is only true as of the moment it was taken, so the drawer dates it.
Posture counts exclude triaged findings. Posture counts roll up open
findings only. Dismissing a finding immediately recomputes the latest scan’s
counts, so the dashboard donut, trend chart, and repo table reflect your real,
actionable risk. The finding itself is always kept — toggle Show triaged on
the repository page to see everything.
What a fix pull request looks like
None of this is configurable, and Settings → Integrations says so rather than offering fields that have nowhere to be stored.
One branch per rule per file is what makes Open a fix PR idempotent: click
it twice and the second click updates the open pull request instead of opening
a second one for the same problem.
The branch name comes from the scan engine, not from this application, so
making it configurable is a change in the public
github.com/raphabot/pipefort module rather than a setting. To fix one rule
across many repositories in a single pass, use a
remediation campaign.
What happens when it closes
If the GitHub App’s webhook is subscribed to Pull request events, closing the PR records the outcome against every finding it covered — merged or closed without merging. That is what lets the Insights funnel count a finding as verified: merged, and then confirmed gone by a later scan. Without the webhook the outcome is never learned. The finding keeps saying a fix was opened on its date and nothing more — which is why “a fix PR was opened” is phrased that way everywhere rather than as “there is an open PR”.Deciding several at once
Tick the rows in the queue — or the box in its header to take the whole filtered list — and the selection bar offers the same verdicts a single finding does: Dismiss… opens the usual sheet, and Snooze the usual durations. One reason is written to every finding in the selection.Use it when the findings are genuinely one decision — the same rule across
one repository, or the same root cause. If they do not share a reason they are
not one decision, and the sheet says so: the reason is the only record of why
any of them was closed, and a batch reason that fits none of them is worse than
no batch at all.
Interaction with rule settings
Rule settings and triage solve different problems: disable a rule when it never applies to you (it stops being detected at all); triage a finding when the rule is right in general but this one occurrence is handled. A finding whose rule is later disabled simply stops appearing and is marked resolved.CLI parity
The CLI’s SARIF output (-o sarif) carries the same fingerprint as each
result’s partialFingerprints, which GitHub code scanning uses to track a
result across commits instead of closing and reopening it when line numbers
shift. Triage states themselves are a web-app feature (GitHub code scanning has
its own dismissal flow for the Action path).