diff --git a/scripts/telemetry_adoption_report.py b/scripts/telemetry_adoption_report.py
index e3e3f22f3b..04e54643d4 100644
--- a/scripts/telemetry_adoption_report.py
+++ b/scripts/telemetry_adoption_report.py
@@ -66,6 +66,28 @@ FEATURE_BOOL_FIELDS = (
("paid_license", "Paid license"),
("has_api_tokens", "Has API tokens"),
)
+DEEP_SIGNAL_FIELDS = (
+ ("agent_hosts", "Agent hosts", "count"),
+ ("docker_containers", "Docker containers", "count"),
+ ("kubernetes_nodes", "Kubernetes nodes", "count"),
+ ("kubernetes_pods", "Kubernetes pods", "count"),
+ ("kubernetes_deployments", "Kubernetes deployments", "count"),
+ ("storage_pools", "Storage pools", "count"),
+ ("physical_disks", "Physical disks", "count"),
+ ("ceph_clusters", "Ceph clusters", "count"),
+ ("network_shares", "Network shares", "count"),
+ ("truenas_systems", "TrueNAS systems", "count"),
+ ("truenas_vms", "TrueNAS VMs", "count"),
+ ("truenas_apps", "TrueNAS apps", "count"),
+ ("vmware_hosts", "VMware hosts", "count"),
+ ("vmware_vms", "VMware VMs", "count"),
+ ("vmware_datastores", "VMware datastores", "count"),
+ ("availability_targets", "Availability targets", "count"),
+ ("patrol_enabled", "Patrol enabled", "bool"),
+ ("discovery_enabled", "Discovery enabled", "bool"),
+ ("notifications_enabled", "Notifications enabled", "bool"),
+ ("ai_actions_enabled", "AI actions enabled", "bool"),
+)
GIT_DESCRIBE_RE = re.compile(
r"^(?P\d+\.\d+\.\d+(?:-[0-9A-Za-z\.-]+)?)-(?P\d+)-g(?P[0-9a-fA-F]+)(?P-dirty)?$"
)
@@ -407,6 +429,60 @@ def summarize_latest_install_windows(
return summary
+def summarize_deep_signal_sources(
+ latest_by_install: dict[str, dict[str, Any]],
+ published_versions: set[str],
+ *,
+ now: datetime | None = None,
+ window: timedelta = timedelta(days=7),
+) -> list[dict[str, Any]]:
+ current_time = now or datetime.now(timezone.utc)
+ by_field: dict[str, dict[str, dict[str, Any]]] = {key: {} for key, _, _ in DEEP_SIGNAL_FIELDS}
+
+ for row in latest_by_install.values():
+ received_at = parse_received_at(str(row["received_at"]))
+ if current_time - received_at > window:
+ continue
+ identity = classify_row_version(row, published_versions)
+
+ for key, _, kind in DEEP_SIGNAL_FIELDS:
+ if kind == "bool":
+ value = 1 if parse_optional_bool(row.get(key)) else 0
+ else:
+ value = parse_optional_nonnegative_int(row.get(key))
+ if value <= 0:
+ continue
+
+ source = by_field[key].setdefault(
+ identity.version,
+ {
+ "version": identity.version,
+ "installs": 0,
+ "total": 0,
+ "is_published_release": identity.is_published_release,
+ },
+ )
+ source["installs"] += 1
+ source["total"] += value
+ source["is_published_release"] = source["is_published_release"] or identity.is_published_release
+
+ result: list[dict[str, Any]] = []
+ for key, label, kind in DEEP_SIGNAL_FIELDS:
+ versions = list(by_field[key].values())
+ if not versions:
+ continue
+ versions.sort(key=lambda source: (-int(source["installs"]), str(source["version"])))
+ result.append(
+ {
+ "field": key,
+ "label": label,
+ "type": kind,
+ "versions": versions,
+ }
+ )
+ return result
+
+
def summarize_rows(
db_stats: dict[str, Any],
rows: Iterable[dict[str, Any]],
@@ -419,15 +495,27 @@ def summarize_rows(
if existing is None or str(row["received_at"]) > str(existing["received_at"]):
latest_by_install[install_id] = row
- latest_install_windows = summarize_latest_install_windows(latest_by_install, published_versions)
+ current_time = datetime.now(timezone.utc)
+ latest_install_windows = summarize_latest_install_windows(
+ latest_by_install,
+ published_versions,
+ now=current_time,
+ )
summary_72h = latest_install_windows["72h"]
+ summary_7d = latest_install_windows["7d"]
return {
"db_stats": db_stats,
"latest_install_windows": latest_install_windows,
+ "deep_signal_sources_7d": summarize_deep_signal_sources(
+ latest_by_install,
+ published_versions,
+ now=current_time,
+ ),
"active_latest": {
"active_24h": latest_install_windows["24h"]["active_installs"],
"active_72h": summary_72h["active_installs"],
+ "active_7d": summary_7d["active_installs"],
},
"latest_version_split_72h": summary_72h["latest_versions"],
"published_version_split_72h": summary_72h["published_versions"],
@@ -484,6 +572,21 @@ def format_text(summary: dict[str, Any], repo: str, since_days: int) -> str:
lines.extend(f" - {entry['label']}: {entry['installs']}" for entry in feature_counts)
else:
lines.append(" - none")
+
+ lines.extend(["", "Deep telemetry signal sources (7d):"])
+ deep_sources = summary.get("deep_signal_sources_7d", [])
+ if deep_sources:
+ for entry in deep_sources:
+ versions = []
+ for source in entry["versions"]:
+ install_word = "install" if source["installs"] == 1 else "installs"
+ source_text = f"{source['version']}: {source['installs']} {install_word}"
+ if entry["type"] == "count":
+ source_text += f", total {source['total']}"
+ versions.append(source_text)
+ lines.append(f"- {entry['label']}: " + "; ".join(versions))
+ else:
+ lines.append("- none")
return "\n".join(lines)
diff --git a/scripts/tests/test_telemetry_adoption_report.py b/scripts/tests/test_telemetry_adoption_report.py
index 95cf5f20a2..c395babf5a 100644
--- a/scripts/tests/test_telemetry_adoption_report.py
+++ b/scripts/tests/test_telemetry_adoption_report.py
@@ -85,6 +85,7 @@ class TelemetryAdoptionReportTest(unittest.TestCase):
"platform": "docker",
"received_at": (now - timedelta(hours=5)).strftime("%Y-%m-%d %H:%M:%S"),
"event": "heartbeat",
+ "agent_hosts": 3,
},
{
"install_id": "install-b",
@@ -99,6 +100,8 @@ class TelemetryAdoptionReportTest(unittest.TestCase):
"platform": "binary",
"received_at": (now - timedelta(days=2, hours=1)).strftime("%Y-%m-%d %H:%M:%S"),
"event": "heartbeat",
+ "agent_hosts": 2,
+ "patrol_enabled": 1,
},
]
@@ -114,6 +117,7 @@ class TelemetryAdoptionReportTest(unittest.TestCase):
self.assertEqual(summary["active_latest"]["active_24h"], 2)
self.assertEqual(summary["active_latest"]["active_72h"], 3)
+ self.assertEqual(summary["active_latest"]["active_7d"], 3)
self.assertEqual(summary["latest_install_windows"]["24h"]["active_installs"], 2)
self.assertEqual(summary["latest_install_windows"]["72h"]["active_installs"], 3)
self.assertEqual(summary["latest_install_windows"]["7d"]["active_installs"], 3)
@@ -146,6 +150,35 @@ class TelemetryAdoptionReportTest(unittest.TestCase):
{"platform": "docker", "installs": 1},
],
)
+ deep_sources = {entry["field"]: entry for entry in summary["deep_signal_sources_7d"]}
+ self.assertEqual(
+ deep_sources["agent_hosts"]["versions"],
+ [
+ {
+ "version": "0.0.0-feature-new-metric",
+ "installs": 1,
+ "total": 2,
+ "is_published_release": False,
+ },
+ {
+ "version": "6.0.0-rc.2",
+ "installs": 1,
+ "total": 3,
+ "is_published_release": False,
+ },
+ ],
+ )
+ self.assertEqual(
+ deep_sources["patrol_enabled"]["versions"],
+ [
+ {
+ "version": "0.0.0-feature-new-metric",
+ "installs": 1,
+ "total": 1,
+ "is_published_release": False,
+ },
+ ],
+ )
def test_format_text_includes_latest_install_windows(self) -> None:
summary = {
@@ -174,6 +207,34 @@ class TelemetryAdoptionReportTest(unittest.TestCase):
"platforms": [{"platform": "binary", "installs": 87}],
},
},
+ "deep_signal_sources_7d": [
+ {
+ "field": "agent_hosts",
+ "label": "Agent hosts",
+ "type": "count",
+ "versions": [
+ {
+ "version": "6.0.0-rc.2",
+ "installs": 4,
+ "total": 18,
+ "is_published_release": False,
+ },
+ ],
+ },
+ {
+ "field": "patrol_enabled",
+ "label": "Patrol enabled",
+ "type": "bool",
+ "versions": [
+ {
+ "version": "6.0.0-rc.2",
+ "installs": 2,
+ "total": 2,
+ "is_published_release": False,
+ },
+ ],
+ },
+ ],
}
rendered = report.format_text(summary, "rcourtman/Pulse", 7)
@@ -183,6 +244,9 @@ class TelemetryAdoptionReportTest(unittest.TestCase):
self.assertIn("Latest install state (7d):", rendered)
self.assertIn(" - 6.0.0-rc.1: 118", rendered)
self.assertIn(" - 6.0.0-rc.2: 32", rendered)
+ self.assertIn("Deep telemetry signal sources (7d):", rendered)
+ self.assertIn("- Agent hosts: 6.0.0-rc.2: 4 installs, total 18", rendered)
+ self.assertIn("- Patrol enabled: 6.0.0-rc.2: 2 installs", rendered)
def test_privacy_docs_keep_relay_mobile_handoff_copy_aligned(self) -> None:
repo_root = Path(__file__).resolve().parents[2]