Skip to main content
Re-scanning a repo used to re-report everything, every time — with no way to tell a fresh problem from the one you looked at last week, or to silence a finding you’ve decided to live with. Pipefort now tracks every finding’s identity across scans and lets you triage it.

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 NEW on 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 as lapsed 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.
Accepted risks are counted on the Insights page as your accepted-risk budget.

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 a PR #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.
No answer at all is its own answer. If the webhook is not configured and the poll has not run, the drawer says nothing has looked inside the pull request — deliberately not the same sentence as “nothing is wrong”. A fix that has sat behind a merge conflict for a week would otherwise be indistinguishable from one that is about to land.
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.
Findings that predate fingerprinting cannot carry a verdict, so they get no checkbox and the queue says how many were left out rather than failing on submit. The API takes one finding at a time, so the app loops it three at a time — the same pattern Scan all uses. That means a batch can partly succeed: if some writes fail, the bar reports how many landed and leaves the rest in the queue to retry, rather than reporting a batch failure that hides the ones that worked.

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).