Findings Persistence
This page covers how findings flow from vega-core audit output through
SharedTriageService deduplication to backend database records.
Finding lifecycle
flowchart TD
A[Agent output] -->|parsed by AuditStage| B[Raw finding]
B -->|finding_updated event| C[vega-backend event handler]
C -->|upsert| D[(Postgres findings table)]
B -->|vega.raw_findings artifact| E[(S3)]
B -->|ScanTransition| F[SharedTriageService.ingest]
F -->|DeduplicatedBugBatch| G[CoreResult.data.deduplicated_bugs]
G -->|finding_deduped event| C
C -->|upsert bug record| D
D --> H[Dashboard findings view]
Raw findings (audit output)
AuditStage emits one finding_updated event per finding:
{
"kind": "finding_updated",
"payload": {
"finding_id": "component_id/hash",
"title": "SQL Injection in query builder",
"severity": "high",
"bug_class": "sql_injection",
"file_path": "src/db/query.py",
"line_start": 87,
"component_id": "db/query_builder",
"scan_id": "scan_xyz"
}
}
Backend upserts finding records from this event. The finding_id is stable for
a given component and finding signature.
Deduplication via SharedTriageService
After all audit components complete, ScanTransition calls
SharedTriageService.ingest(context, batch). The service:
- Receives raw findings from
RawFindingBatch. - Groups findings by bug signature (file path + bug class + line range).
- Assigns stable
bug_idvalues to deduplicated bugs. - Returns a
DeduplicatedBugBatchwith unique bugs. - Emits
finding_dedupedevents for backend to record associations.
The deduplicated bugs are included in CoreResult.data["deduplicated_bugs"] and
written to the vega.deduplicated_bugs artifact.
Verification status
After verify_and_patch runs, VerifyStage emits finding_verified events:
{
"kind": "finding_verified",
"payload": {
"finding_id": "bug_id",
"status": "verified",
"confidence": "high",
"triggerable": true,
"root_cause": "Missing authorization check..."
}
}
Backend updates findings.verification_status from this event.
Possible verification statuses:
| Status | Meaning |
|---|---|
null |
Not yet verified |
"verified" |
Reproducible — eligible for patch |
"not_verified" |
Agent could not reproduce |
"insufficient_evidence" |
Unclear — needs review |
Patch status
After PatchStage completes, patch_finding_completed events report per-bug
patch results:
{
"kind": "patch_finding_completed",
"payload": {
"finding_id": "bug_id",
"status": "accepted",
"confidence": "high"
}
}
And a final patch_created event with the artifact ref:
{
"kind": "patch_created",
"payload": {
"patch": {"uri": "s3://...", "kind": "patch", ...},
"accepted_patch_count": 1,
"rejected_patch_count": 0,
"skipped_unverified_count": 0
}
}
For first-class patch status in the UI, backend should add patch fields to the
findings table: patch_status, patch_artifact_ref, patch_result,
patch_updated_at.
Multi-shard deduplication
When multiple scan runner containers process different component shards, each runner produces its own raw findings. Backend is responsible for aggregating these and running a final deduplication pass, or coordinating a shared triage process before final bug records are committed.
The current in-process SharedTriageService handles single-runner scans
automatically. Multi-runner deduplication coordination is a backend
responsibility.
Debugging missing findings
- Was
raw_findings_readyemitted? Checkfinding_countin the payload. - Was
finding_updatedemitted per component? If not, the audit stage may have had parse errors — checkagent_logevents. - Was
raw_findings_batch_receivedemitted by triage? If not, checkScanTransitionwiring. - Check
vega.raw_findingsartifact in S3 / local store. - Check
CoreResult.data["deduplicated_bugs"]length. - If findings appear in raw but not in deduplication, check triage logic and bug signature matching.