CA: report imputation in multi-VC comparisons when verbose

Imputation was silent, so a result whose top max_delta rows are entirely
presence/absence artifacts looked no different from one built on observed
scores throughout. Pass the connection's verbosity into
add_multi_vc_comparisons() and report how many node/collocate combinations
and label cells were imputed, pointing at the `imputed` column and
queryMissingScores.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Change-Id: I5159483452595f38d09f06411b64a7327814977f
diff --git a/R/collocationAnalysis.R b/R/collocationAnalysis.R
index b5b1ded..b949e2c 100644
--- a/R/collocationAnalysis.R
+++ b/R/collocationAnalysis.R
@@ -256,7 +256,8 @@
 
         multi_result |>
           add_multi_vc_comparisons(
-            missingScoreQuantile = missingScoreQuantile
+            missingScoreQuantile = missingScoreQuantile,
+            verbose = kco@verbose
           )
       }
     } else {
@@ -574,7 +575,7 @@
   sprintf("contains(<%s>, (%s))", span, combined)
 }
 
-add_multi_vc_comparisons <- function(result, missingScoreQuantile = 0.05) {
+add_multi_vc_comparisons <- function(result, missingScoreQuantile = 0.05, verbose = FALSE) {
   label <- node <- collocate <- vc <- webUIRequestUrl <- NULL
 
   if (!"label" %in% names(result) || dplyr::n_distinct(result$label) < 2) {
@@ -1288,6 +1289,22 @@
   }
   comparison$imputed <- comparison$n_imputed > 0L
 
+  n_imputed_rows <- sum(comparison$imputed)
+  if (n_imputed_rows > 0) {
+    log_info(verbose, sprintf(
+      paste0(
+        "Imputed scores for %d of %d node/collocate combinations (%d of %d label cells) ",
+        "that are not attested in every virtual corpus. Their delta and winner/loser ",
+        "columns reflect presence vs. absence rather than a measured contrast; see the ",
+        "`imputed` column and `queryMissingScores`.\n"
+      ),
+      n_imputed_rows,
+      nrow(comparison),
+      sum(comparison$n_imputed),
+      nrow(comparison) * length(labels)
+    ))
+  }
+
   collapse_consensus_url_columns <- function(url_cols) {
     if (length(url_cols) == 0) {
       return(rep(NA_character_, nrow(comparison)))
diff --git a/tests/testthat/test-collocations.R b/tests/testthat/test-collocations.R
index 183de99..2094dbd 100644
--- a/tests/testthat/test-collocations.R
+++ b/tests/testthat/test-collocations.R
@@ -372,6 +372,35 @@
   expect_lte(unique(c1$loser_logDice_value), min(sample_result$logDice))
 })
 
+test_that("add_multi_vc_comparisons reports imputation when verbose", {
+  sample_result <- tibble::tibble(
+    node = c("n", "n", "n"),
+    collocate = c("c1", "c2", "c2"),
+    vc = c("vc1", "vc1", "vc2"),
+    label = c("A", "A", "B"),
+    N = rep(100, 3),
+    O = c(10, 10, 20),
+    O1 = rep(50, 3),
+    O2 = rep(30, 3),
+    E = rep(5, 3),
+    w = rep(2, 3),
+    leftContextSize = rep(1, 3),
+    rightContextSize = rep(1, 3),
+    frequency = c(10, 10, 20),
+    logDice = c(6, 5, 7),
+    pmi = c(3, 2, 4)
+  )
+
+  output <- capture.output(
+    RKorAPClient:::add_multi_vc_comparisons(sample_result, verbose = TRUE)
+  )
+  expect_match(paste(output, collapse = " "), "Imputed scores for 1 of 2")
+  expect_match(paste(output, collapse = " "), "queryMissingScores")
+
+  # nothing is printed unless verbose
+  expect_silent(RKorAPClient:::add_multi_vc_comparisons(sample_result, verbose = FALSE))
+})
+
 test_that("add_multi_vc_comparisons reports no imputation when all labels are complete", {
   sample_result <- tibble::tibble(
     node = rep("n", 4),