feat(cli): add 'lore related' semantic similarity command (bd-8con)

Adds 'lore related' / 'lore similar' command for discovering semantically
related issues and MRs using vector embeddings.

Two modes:
- Entity mode: find entities similar to a specific issue/MR
- Query mode: embed free text and find matching entities

Includes distance-to-similarity conversion, label intersection,
human and robot output formatters, and 11 unit tests.
This commit is contained in:
teernisse
2026-02-19 08:01:55 -05:00
parent 3e96f19a11
commit c8dece8c60
27 changed files with 4066 additions and 33 deletions

View File

@@ -20,6 +20,67 @@ impl SyncRunRecorder {
Ok(Self { row_id })
}
/// Returns the database row ID for this sync run.
pub fn row_id(&self) -> i64 {
self.row_id
}
/// Set surgical-specific metadata after `start()`.
///
/// Takes `&self` so the recorder can continue to be used for phase
/// updates and entity result recording before finalization.
pub fn set_surgical_metadata(
&self,
conn: &Connection,
mode: &str,
phase: &str,
iids_json: &str,
) -> Result<()> {
conn.execute(
"UPDATE sync_runs SET mode = ?1, phase = ?2, surgical_iids_json = ?3
WHERE id = ?4",
rusqlite::params![mode, phase, iids_json, self.row_id],
)?;
Ok(())
}
/// Update the pipeline phase and refresh the heartbeat timestamp.
pub fn update_phase(&self, conn: &Connection, phase: &str) -> Result<()> {
conn.execute(
"UPDATE sync_runs SET phase = ?1, heartbeat_at = ?2 WHERE id = ?3",
rusqlite::params![phase, now_ms(), self.row_id],
)?;
Ok(())
}
/// Increment a surgical counter column for the given entity type and stage.
///
/// Unknown `(entity_type, stage)` combinations are silently ignored.
/// Column names are derived from a hardcoded match — no SQL injection risk.
pub fn record_entity_result(
&self,
conn: &Connection,
entity_type: &str,
stage: &str,
) -> Result<()> {
let column = match (entity_type, stage) {
("issue", "fetched") => "issues_fetched",
("issue", "ingested") => "issues_ingested",
("mr", "fetched") => "mrs_fetched",
("mr", "ingested") => "mrs_ingested",
("issue" | "mr", "skipped_stale") => "skipped_stale",
("doc", "regenerated") => "docs_regenerated",
("doc", "embedded") => "docs_embedded",
(_, "warning") => "warnings_count",
_ => return Ok(()),
};
conn.execute(
&format!("UPDATE sync_runs SET {column} = {column} + 1 WHERE id = ?1"),
rusqlite::params![self.row_id],
)?;
Ok(())
}
pub fn succeed(
self,
conn: &Connection,
@@ -63,6 +124,18 @@ impl SyncRunRecorder {
)?;
Ok(())
}
/// Finalize the run as cancelled. Consumes self to prevent further use.
pub fn cancel(self, conn: &Connection, reason: &str) -> Result<()> {
let now = now_ms();
conn.execute(
"UPDATE sync_runs SET finished_at = ?1, cancelled_at = ?2,
status = 'cancelled', error = ?3
WHERE id = ?4",
rusqlite::params![now, now, reason, self.row_id],
)?;
Ok(())
}
}
#[cfg(test)]