feat(agent): cache control embedding index + auto-wire semantic pass (gated) (#217)
CI / Check (push) Skipped
CI / Detect Changes (push) Successful in 3s
CI / Deploy Docs (push) Skipped
CI / Deploy Agent (push) Failing after 3s
CI / Deploy Dashboard (push) Failing after 3s
CI / Deploy MCP (push) Failing after 2s

This commit was merged in pull request #217.
This commit is contained in:
2026-07-21 11:29:16 +00:00
parent f516ecf3b5
commit 0ef2cd1b23
6 changed files with 176 additions and 5 deletions
+8 -4
View File
@@ -128,9 +128,11 @@ fn fetch_region(repo_path: &Path, file: &str, line: u32) -> Option<CandidateRegi
/// ~13.6k master-control corpus (which has no CWE to LUT on). Returns the number
/// of findings that gained a master-control ref.
///
/// Opt-in: the orchestrator does not run this yet. It builds the control embedding
/// index per call (embeds the whole corpus) — production should cache/persist that
/// index rather than rebuild it each scan.
/// Gated: the orchestrator runs this only when `breakpilot.semantic_mapping` is
/// set (default off, flipped on once the master-controls catalog is live). The
/// control embedding index is built once and cached to `snapshot_dir` keyed by
/// corpus hash ([`ControlIndex::load_or_build`]), so only the first scan after a
/// catalog change pays the embedding cost.
pub async fn semantic_stamp_findings(
config: &AgentConfig,
llm: Arc<LlmClient>,
@@ -164,7 +166,9 @@ pub async fn semantic_stamp_findings(
severity: Severity::Medium,
})
.collect();
let index = match ControlIndex::build(&llm, specs).await {
let cache_path =
Path::new(&config.breakpilot.snapshot_dir).join("control-index-master-controls.json");
let index = match ControlIndex::load_or_build(&llm, specs, &cache_path).await {
Ok(i) if !i.is_empty() => i,
Ok(_) => return 0,
Err(e) => {