diff --git a/compliance-agent/src/pipeline/orchestrator.rs b/compliance-agent/src/pipeline/orchestrator.rs index da92ade..8f038ea 100644 --- a/compliance-agent/src/pipeline/orchestrator.rs +++ b/compliance-agent/src/pipeline/orchestrator.rs @@ -277,8 +277,10 @@ impl PipelineOrchestrator { } } - // Dedup against existing findings and insert new ones + // Dedup against existing findings: insert first-seen ones, and refresh the + // control mappings on ones we've seen before. let mut new_count = 0u32; + let mut refreshed_count = 0u32; let mut new_findings: Vec = Vec::new(); for mut finding in all_findings { finding.scan_run_id = Some(scan_run_id.to_string()); @@ -293,8 +295,25 @@ impl PipelineOrchestrator { finding.id = result.inserted_id.as_object_id(); new_findings.push(finding); new_count += 1; + } else if !finding.control_refs.is_empty() { + // Re-scan refresh: a mapping pass (newly enabled or tuned) computed + // control_refs for a finding first seen before mapping ran. Persist + // them onto the existing row — the insert path alone never would. + self.db + .findings() + .update_one( + doc! { "fingerprint": &finding.fingerprint }, + doc! { "$set": { "control_refs": finding.control_refs.clone() } }, + ) + .await?; + refreshed_count += 1; } } + if refreshed_count > 0 { + tracing::info!( + "[{repo_id}] Refreshed control_refs on {refreshed_count} existing findings" + ); + } // Remove stale SBOM entries for this repo before reinserting if !sbom_entries.is_empty() {