Expand stats coverage summary

ober

733988cc8adfdce6004aaba08883647a609885bf

diff --git a/README.md b/README.md
index c98e8af..73b895c 100644
--- a/README.md
+++ b/README.md
@@ -59,6 +59,10 @@ JSON output. `--llm` is accepted as an explicit request, but this release has no
 active provider adapter; it emits a warning and still reports `llm_used:false`
 and `network_used:false`.
 
+`stats` reports commit count, distinct authors, recorded tools, recorded AI line
+counts, metadata/heuristic counts, score-band counts, and signal-category
+coverage for the selected revision range.
+
 Merge commits default to first-parent diffs. Use `--all-parents` to inspect
 per-parent merge diffs via Git's `-m` mode; merge warnings state which mode was
 used.
diff --git a/main-binary.ss b/main-binary.ss
index 45aa76d..b3740aa 100644
--- a/main-binary.ss
+++ b/main-binary.ss
@@ -745,11 +745,48 @@
       (begin
         (displayln "warnings:")
         (for ([w (finding-warnings f)]) (displayln (str "- " w))))))
+(def stats-verdicts '("recorded-ai-authorship" "metadata-indicated-agent" "likely-ai-assisted" "mixed-uncertain" "likely-human-style" "insufficient-evidence"))
+(def stats-signal-categories '("text" "code" "structure" "similarity" "history" "baseline"))
+
+(def (alist-ref/default pairs name fallback)
+  (let ([hit (assoc name pairs)])
+    (if hit (cdr hit) fallback)))
+
+(def (attribution-line-count item)
+  (let ([start (alist-ref/default item 'start 0)]
+        [end (alist-ref/default item 'end 0)])
+    (if (and (number? start) (number? end) (>= end start))
+        (+ 1 (- end start))
+        0)))
+
+(def (finding-recorded-line-count f)
+  (sum (map attribution-line-count (finding-attribution f))))
+
+(def (finding-attribution-tools f)
+  (unique (filter (lambda (tool) (not (string-empty? tool)))
+                  (map (lambda (item) (alist-ref/default item 'tool "")) (finding-attribution f)))))
+
+(def (stats-tools findings)
+  (unique (append-map finding-attribution-tools findings)))
+
+(def (finding-has-signal-category? f category)
+  (for/or ([s (finding-signals f)])
+    (same-public-string? (signal-category s) category)))
 (def (display-stats findings)
-  (displayln (str "commits: " (length findings)))
-  (displayln (str "recorded AI notes: " (count-where (lambda (f) (not (string-empty? (finding-note f)))) findings)))
-  (displayln (str "metadata indicated agents: " (count-where (lambda (f) (pair? (finding-metadata f))) findings)))
-  (displayln (str "likely AI-assisted by heuristics: " (count-where (lambda (f) (same-public-string? (finding-verdict f) "likely-ai-assisted")) findings))))
+  (let ([tools (stats-tools findings)])
+    (displayln (str "commits: " (length findings)))
+    (displayln (str "authors: " (length (unique (map finding-author-email findings)))))
+    (displayln (str "tools: " (if (null? tools) "none" (string-join tools ","))))
+    (displayln (str "recorded AI notes: " (count-where (lambda (f) (not (string-empty? (finding-note f)))) findings)))
+    (displayln (str "recorded AI lines: " (sum (map finding-recorded-line-count findings))))
+    (displayln (str "metadata indicated agents: " (count-where (lambda (f) (pair? (finding-metadata f))) findings)))
+    (displayln (str "likely AI-assisted by heuristics: " (count-where (lambda (f) (same-public-string? (finding-verdict f) "likely-ai-assisted")) findings)))
+    (displayln "score bands:")
+    (for ([v stats-verdicts])
+      (displayln (str "  " v ": " (count-where (lambda (f) (same-public-string? (finding-verdict f) v)) findings))))
+    (displayln "signal coverage:")
+    (for ([category stats-signal-categories])
+      (displayln (str "  " category ": " (count-where (lambda (f) (finding-has-signal-category? f category)) findings))))))
 
 (def (default-options) (make-options "scan" "." 50 "table" #f #f #f #f 0 #f #f))
 (def (config-value obj field fallback)
diff --git a/tests/fixture-smoke.sh b/tests/fixture-smoke.sh
index 38db1a4..5f55a44 100755
--- a/tests/fixture-smoke.sh
+++ b/tests/fixture-smoke.sh
@@ -86,7 +86,14 @@ printf '%s\n' "$metadata" | grep -q '"score":0.0'
 
 stats=$("$root/bin/jerboa-aigit" stats "$fixture" --count 2)
 printf '%s\n' "$stats" | grep -q 'commits: 2'
+printf '%s\n' "$stats" | grep -q 'authors: 2'
+printf '%s\n' "$stats" | grep -q 'tools: codex'
 printf '%s\n' "$stats" | grep -q 'recorded AI notes: 1'
+printf '%s\n' "$stats" | grep -q 'recorded AI lines: 360'
+printf '%s\n' "$stats" | grep -q 'score bands:'
+printf '%s\n' "$stats" | grep -q '  recorded-ai-authorship: 1'
+printf '%s\n' "$stats" | grep -q 'signal coverage:'
+printf '%s\n' "$stats" | grep -q '  code: 2'
 
 jsonl=$("$root/bin/jerboa-aigit" scan "$fixture" --format jsonl --count 2)
 jsonl_lines=$(printf '%s\n' "$jsonl" | wc -l | tr -d ' ')