From 87bdbda46865665da60175e87fcab2c52fa134e6 Mon Sep 17 00:00:00 2001 From: teernisse Date: Wed, 25 Feb 2026 10:02:37 -0500 Subject: [PATCH] feat(status): add per-entity sync counts from migration 027 Enhances sync status reporting to include granular per-entity counts that were added in database migration 027. This provides better visibility into what each sync run actually processed. New fields in SyncRunInfo and robot mode JSON: - issues_fetched / issues_ingested: issue sync counts - mrs_fetched / mrs_ingested: merge request sync counts - skipped_stale: entities skipped due to staleness - docs_regenerated / docs_embedded: document pipeline counts - warnings_count: non-fatal issues during sync Robot mode optimization: - Uses skip_serializing_if = "is_zero" to omit zero-value fields - Reduces JSON payload size for typical sync runs - Maintains backwards compatibility (fields are additive) SQL query now reads all 8 new columns from sync_runs table, with defensive unwrap_or(0) for NULL handling. Co-Authored-By: Claude Opus 4.5 --- src/cli/commands/sync_status.rs | 50 ++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/src/cli/commands/sync_status.rs b/src/cli/commands/sync_status.rs index af4a70c..56c652c 100644 --- a/src/cli/commands/sync_status.rs +++ b/src/cli/commands/sync_status.rs @@ -12,6 +12,10 @@ use crate::core::time::{format_full_datetime, ms_to_iso}; const RECENT_RUNS_LIMIT: usize = 10; +fn is_zero(value: &i64) -> bool { + *value == 0 +} + #[derive(Debug)] pub struct SyncRunInfo { pub id: i64, @@ -24,6 +28,15 @@ pub struct SyncRunInfo { pub total_items_processed: i64, pub total_errors: i64, pub stages: Option>, + // Per-entity counts (from migration 027) + pub issues_fetched: i64, + pub issues_ingested: i64, + pub mrs_fetched: i64, + pub mrs_ingested: i64, + pub skipped_stale: i64, + pub docs_regenerated: i64, + pub docs_embedded: i64, + pub warnings_count: i64, } #[derive(Debug)] @@ -68,7 +81,9 @@ pub fn run_sync_status(config: &Config) -> Result { fn get_recent_sync_runs(conn: &Connection, limit: usize) -> Result> { let mut stmt = conn.prepare( "SELECT id, started_at, finished_at, status, command, error, - run_id, total_items_processed, total_errors, metrics_json + run_id, total_items_processed, total_errors, metrics_json, + issues_fetched, issues_ingested, mrs_fetched, mrs_ingested, + skipped_stale, docs_regenerated, docs_embedded, warnings_count FROM sync_runs ORDER BY started_at DESC LIMIT ?1", @@ -91,6 +106,14 @@ fn get_recent_sync_runs(conn: &Connection, limit: usize) -> Result>(7)?.unwrap_or(0), total_errors: row.get::<_, Option>(8)?.unwrap_or(0), stages, + issues_fetched: row.get::<_, Option>(10)?.unwrap_or(0), + issues_ingested: row.get::<_, Option>(11)?.unwrap_or(0), + mrs_fetched: row.get::<_, Option>(12)?.unwrap_or(0), + mrs_ingested: row.get::<_, Option>(13)?.unwrap_or(0), + skipped_stale: row.get::<_, Option>(14)?.unwrap_or(0), + docs_regenerated: row.get::<_, Option>(15)?.unwrap_or(0), + docs_embedded: row.get::<_, Option>(16)?.unwrap_or(0), + warnings_count: row.get::<_, Option>(17)?.unwrap_or(0), }) })? .collect(); @@ -198,6 +221,23 @@ struct SyncRunJsonInfo { error: Option, #[serde(skip_serializing_if = "Option::is_none")] stages: Option>, + // Per-entity counts + #[serde(skip_serializing_if = "is_zero")] + issues_fetched: i64, + #[serde(skip_serializing_if = "is_zero")] + issues_ingested: i64, + #[serde(skip_serializing_if = "is_zero")] + mrs_fetched: i64, + #[serde(skip_serializing_if = "is_zero")] + mrs_ingested: i64, + #[serde(skip_serializing_if = "is_zero")] + skipped_stale: i64, + #[serde(skip_serializing_if = "is_zero")] + docs_regenerated: i64, + #[serde(skip_serializing_if = "is_zero")] + docs_embedded: i64, + #[serde(skip_serializing_if = "is_zero")] + warnings_count: i64, } #[derive(Serialize)] @@ -237,6 +277,14 @@ pub fn print_sync_status_json(result: &SyncStatusResult, elapsed_ms: u64) { total_errors: run.total_errors, error: run.error.clone(), stages: run.stages.clone(), + issues_fetched: run.issues_fetched, + issues_ingested: run.issues_ingested, + mrs_fetched: run.mrs_fetched, + mrs_ingested: run.mrs_ingested, + skipped_stale: run.skipped_stale, + docs_regenerated: run.docs_regenerated, + docs_embedded: run.docs_embedded, + warnings_count: run.warnings_count, } }) .collect();