& { answerCount: number };
+
+export function CommandUsefulnessPanel({
+ totals,
+ commands,
+ windowDays,
+}: {
+ totals: CommandUsefulnessTotals;
+ commands: CommandBucket[];
+ windowDays: number;
+}) {
+ return (
+
+ Command usefulness
+
+ Maintainer feedback on GitHub command answers — separate from security audit events.
+
+
+ last {windowDays} days}
+ />
+ positive signal}
+ />
+ answers: {totals.answerCount}}
+ />
+ improvement signal}
+ />
+
+ {commands.length > 0 ? (
+
+
+
+
+ | Command |
+ Feedback |
+ Useful |
+ Rate |
+
+
+
+ {commands.slice(0, 8).map((row) => (
+
+ | {row.command} |
+ {row.feedbackCount} |
+ {row.usefulCount} |
+
+ {row.usefulnessRate === null ? "—" : `${Math.round(row.usefulnessRate * 100)}%`}
+ |
+
+ ))}
+
+
+
+ ) : (
+
+ No command feedback recorded in this window.
+
+ )}
+
+ );
+}
+
+export function ProductUsageBreakdownPanel({
+ byEvent,
+ bySurface,
+ byTool = [],
+}: {
+ byEvent: Array<{ eventName: string; count: number }>;
+ bySurface: Array<{ surface: string; count: number }>;
+ byTool?: Array<{ key: string; count: number }>;
+}) {
+ const highlights = pickUsageHighlights(byEvent, byTool);
+ return (
+
+ Product usage breakdown
+
+ MCP commands, GitHub commands, PR packets, quiet skips, decision-pack tools, and drift
+ signals (7-day window).
+
+
+ {highlights.map((item) => (
+ {item.detail}}
+ />
+ ))}
+
+
+ ({ key: row.eventName, count: row.count }))}
+ />
+ ({ key: row.surface, count: row.count }))}
+ />
+
+
+ );
+}
+
+function DimensionList({
+ title,
+ rows,
+}: {
+ title: string;
+ rows: Array<{ key: string; count: number; hint?: string }>;
+}) {
+ return (
+
+
{title}
+
+ {rows.length > 0 ? (
+ rows.map((row) => (
+
+ {row.key}
+ {row.count}
+
+ ))
+ ) : (
+
No data
+ )}
+
+
+ );
+}
+
+function pickUsageHighlights(
+ byEvent: Array<{ eventName: string; count: number }>,
+ byTool: Array<{ key: string; count: number }>,
+) {
+ const sum = (names: string[]) =>
+ byEvent
+ .filter((row) => names.includes(row.eventName))
+ .reduce((total, row) => total + row.count, 0);
+ const mcp = sum(["mcp_request", "mcp_tool_called"]);
+ const github = sum(["agent_command_replied", "agent_command_skipped"]);
+ const quietSkips = sum(["agent_command_skipped"]);
+ const prPackets = sum(["agent_pr_packet_completed"]);
+ const preflights = sum(["agent_preflight_branch_completed", "local_branch_analysis_completed"]);
+ const decisionPacks = byTool
+ .filter((row) => row.key.includes("decision_pack"))
+ .reduce((total, row) => total + row.count, 0);
+ const driftSignals = sum(["upstream_drift_detected", "upstream_drift_filed"]);
+ return [
+ { id: "mcp", label: "MCP usage", value: mcp, detail: "requests + tool calls" },
+ { id: "github", label: "GitHub commands", value: github, detail: "replies + quiet skips" },
+ { id: "quiet", label: "Quiet skips", value: quietSkips, detail: "intentional no-reply" },
+ { id: "packets", label: "PR packets", value: prPackets, detail: "completed packets" },
+ {
+ id: "preflight",
+ label: "PR preflights",
+ value: preflights,
+ detail: "branch preflight events",
+ },
+ {
+ id: "decision",
+ label: "Decision packs",
+ value: decisionPacks,
+ detail: "MCP decision-pack tool calls",
+ },
+ {
+ id: "drift",
+ label: "Drift incidents",
+ value: driftSignals,
+ detail: "upstream drift product events",
+ },
+ ];
+}
+
+function formatRetentionWindow(window: string): string {
+ if (window === "previous_7_days") return "7-day retention";
+ if (window === "previous_30_days") return "30-day retention";
+ return window.replaceAll("_", " ");
+}
+
+function formatPercent(rate: number): string {
+ return `${Math.round(rate * 1000) / 10}%`;
+}
diff --git a/apps/gittensory-ui/src/routes/app.analytics.tsx b/apps/gittensory-ui/src/routes/app.analytics.tsx
index 317fc6c16d..b708c9a6c4 100644
--- a/apps/gittensory-ui/src/routes/app.analytics.tsx
+++ b/apps/gittensory-ui/src/routes/app.analytics.tsx
@@ -3,6 +3,12 @@ import { createFileRoute } from "@tanstack/react-router";
import { BoundaryBadge, Stat, StatusPill } from "@/components/site/control-primitives";
import { StateBoundary } from "@/components/site/state-views";
import { TrendChart } from "@/components/site/trend-chart";
+import {
+ AdoptionRetentionPanel,
+ CommandUsefulnessPanel,
+ ProductUsageBreakdownPanel,
+ WeeklyValueMetricsPanel,
+} from "@/components/site/usage-analytics-panels";
import { useApiResource } from "@/lib/api/use-api-resource";
export const Route = createFileRoute("/app/analytics")({
@@ -12,6 +18,12 @@ export const Route = createFileRoute("/app/analytics")({
type OperatorDashboard = {
metrics: Array<{ label: string; value: string; delta: string }>;
noiseReduction: Array<{ label: string; value: number; spark: number[] }>;
+ usageSummary?: {
+ totalEvents: number;
+ activeActors: number;
+ byEvent: Array<{ eventName: string; count: number }>;
+ bySurface: Array<{ surface: string; count: number }>;
+ };
usageRollupStatus?: {
status: "empty" | "ready" | "partial" | "stale" | "incomplete";
latestRollupDay?: string | null;
@@ -23,11 +35,53 @@ type OperatorDashboard = {
totalEvents: number;
activeActors: number;
activeRepos: number;
+ byRole: Array<{ role: string; count: number; activeActors: number; activeRepos: number }>;
+ activationByRole: Array<{
+ role: string;
+ firstUsefulActionActors: number;
+ doctorPassActors: number;
+ }>;
+ retention: Array<{
+ window: string;
+ activeActors: number;
+ retainedActors: number;
+ retentionRate: number;
+ capped: boolean;
+ byRole: Array<{
+ role: string;
+ activeActors: number;
+ retainedActors: number;
+ retentionRate: number;
+ }>;
+ }>;
+ byTool?: Array<{ key: string; count: number }>;
activation: {
fullyActivatedActors: number;
githubActivatedRepos: number;
};
}>;
+ weeklyValueReport?: {
+ metrics: Array<{ id: string; label: string; value: number; detail: string }>;
+ warnings: string[];
+ freshness: { status: string; latestRollupDay?: string | null };
+ };
+ commandUsefulness?: {
+ windowDays: number;
+ totals: {
+ feedbackCount: number;
+ usefulCount: number;
+ notUsefulCount: number;
+ answerCount: number;
+ usefulnessRate: number | null;
+ };
+ commands: Array<{
+ command: string;
+ feedbackCount: number;
+ usefulCount: number;
+ notUsefulCount: number;
+ usefulnessRate: number | null;
+ }>;
+ };
mcpCompatibilityAdoption?: {
totalEvents: number;
activeActors: number;
@@ -43,6 +97,7 @@ type OperatorDashboard = {
count: number;
}>;
};
+ upstreamDrift?: { status?: string; openReportCount?: number } | null;
};
function ProductAnalytics() {
@@ -51,6 +106,10 @@ function ProductAnalytics() {
"Product analytics",
);
const data = dashboard.status === "ready" ? dashboard.data : null;
+ const latestRollup =
+ data?.usageRollups && data.usageRollups.length > 0
+ ? [...data.usageRollups].sort((a, b) => b.day.localeCompare(a.day))[0]
+ : null;
return (
- Product analytics
+ Usage & value analytics
- Aggregate deployment, session, digest, and installation metrics from the live API.
+ Operator-facing adoption, activation, retention, and ecosystem value from product
+ usage rollups — not security audit logs or private source data.
@@ -91,6 +151,11 @@ function ProductAnalytics() {
>
{data.usageRollupStatus?.status ?? "Live API"}
+ {data.upstreamDrift?.status ? (
+
+ Drift · {data.upstreamDrift.status}
+
+ ) : null}
@@ -106,6 +171,37 @@ function ProductAnalytics() {
))}
+ {data.weeklyValueReport ? (
+
+ ) : null}
+
+ {data.usageSummary ? (
+
+ ) : null}
+
+ {latestRollup ? (
+
+ ) : null}
+
+ {data.commandUsefulness ? (
+
+ ) : null}
+
Operational trend signals
@@ -218,6 +314,13 @@ function ProductAnalytics() {
{data.usageRollupStatus?.latestRollupDay ?? "current"}
+ {data.usageRollupStatus?.warnings.length ? (
+
+ {data.usageRollupStatus.warnings.slice(0, 4).map((warning) => (
+ - · {warning}
+ ))}
+
+ ) : null}
diff --git a/src/api/routes.ts b/src/api/routes.ts
index 3796613244..94bd6d82c4 100644
--- a/src/api/routes.ts
+++ b/src/api/routes.ts
@@ -137,6 +137,7 @@ import {
LATEST_RECOMMENDED_MCP_VERSION,
MINIMUM_SUPPORTED_MCP_VERSION,
} from "../services/mcp-compatibility";
+import { buildOperatorDashboardPayload } from "../services/operator-dashboard";
import {
buildWeeklyValueReport,
formatWeeklyValueReportMarkdown,
@@ -840,87 +841,7 @@ export function createApp() {
app.get("/v1/app/operator-dashboard", async (c) => {
const forbidden = await requireAppRole(c, ["operator"]);
if (forbidden) return forbidden;
- const usageSince = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString();
- const [
- repositories,
- installations,
- health,
- registry,
- scoring,
- upstreamDrift,
- activeSessions,
- digestSubscriptions,
- rateLimits,
- usageSummary,
- usageRollups,
- usageRollupStatus,
- mcpCompatibilityAdoption,
- commandUsefulness,
- ] = await Promise.all([
- listRepositories(c.env),
- listInstallations(c.env),
- listInstallationHealth(c.env),
- getLatestRegistrySnapshot(c.env),
- getLatestScoringModelSnapshot(c.env),
- loadUpstreamStatus(c.env),
- countActiveAuthSessions(c.env),
- countActiveDigestSubscriptions(c.env),
- listLatestGitHubRateLimitObservations(c.env, 20),
- summarizeProductUsageEvents(c.env, usageSince),
- listProductUsageDailyRollups(c.env, { limit: 14 }),
- getProductUsageRollupStatus(c.env),
- summarizeMcpCompatibilityAdoption(c.env, usageSince),
- getCommandUsefulnessSummary(c.env),
- ]);
- const weeklyValueReport = buildWeeklyValueReport({
- generatedAt: nowIso(),
- variant: "operator",
- days: 7,
- repositories,
- installations,
- health,
- registry,
- scoring,
- upstreamDrift,
- usageSummary,
- usageRollups,
- usageRollupStatus,
- activeSessions,
- digestSubscriptions,
- });
- const installedRepos = repositories.filter((repo) => repo.isInstalled).length;
- const registeredRepos = repositories.filter((repo) => repo.isRegistered).length;
- return c.json({
- generatedAt: nowIso(),
- metrics: [
- { label: "Active sessions", value: String(activeSessions), delta: "browser + CLI/MCP" },
- { label: "Installations", value: String(installations.length), delta: `${installedRepos} installed repos` },
- { label: "Registered repos", value: String(registeredRepos), delta: registry ? `${registry.repoCount} in latest registry` : "registry missing" },
- { label: "Digest subscriptions", value: String(digestSubscriptions), delta: "store-only" },
- { label: "Product events", value: String(usageSummary.totalEvents), delta: "last 7 days" },
- { label: "Active users", value: String(usageSummary.activeActors), delta: "hashed, last 7 days" },
- { label: "Activation rollups", value: usageRollupStatus.status, delta: usageRollupStatus.latestRollupDay ?? "not generated" },
- { label: "MCP stale clients", value: String(mcpCompatibilityAdoption.staleEvents + mcpCompatibilityAdoption.incompatibleEvents), delta: `${mcpCompatibilityAdoption.totalEvents} MCP event(s)` },
- { label: "Command usefulness", value: `${commandUsefulness.totals.usefulCount}/${commandUsefulness.totals.feedbackCount}`, delta: usefulnessDelta(commandUsefulness.totals.usefulnessRate) },
- { label: "Install issues", value: String(health.filter((record) => record.status !== "healthy").length), delta: "current health cache" },
- { label: "Rate-limit events", value: String(rateLimits.length), delta: "latest observations" },
- ],
- noiseReduction: [
- { label: "Healthy installations", value: health.filter((record) => record.status === "healthy").length, spark: sparklineFromCounts(health.filter((record) => record.status === "healthy").length, Math.max(health.length, 1)) },
- { label: "Registered coverage", value: registeredRepos, spark: sparklineFromCounts(registeredRepos, Math.max(repositories.length, 1)) },
- { label: "Installed coverage", value: installedRepos, spark: sparklineFromCounts(installedRepos, Math.max(repositories.length, 1)) },
- ],
- weeklyReport: weeklyValueReport.summary,
- weeklyValueReport,
- usageSummary,
- usageRollups,
- usageRollupStatus,
- mcpCompatibilityAdoption,
- commandUsefulness,
- registry,
- scoringModel: scoring,
- upstreamDrift,
- });
+ return c.json(await buildOperatorDashboardPayload(c.env));
});
app.get("/v1/app/notification-model", async (c) => {
@@ -2667,10 +2588,6 @@ function sampleMinerSnapshot(login: string) {
};
}
-function usefulnessDelta(rate: number | null): string {
- return rate === null ? "no feedback yet" : `${Math.round(rate * 100)}% useful over 30 days`;
-}
-
function clampInteger(value: number, min: number, max: number): number {
if (!Number.isFinite(value)) return min;
return Math.min(max, Math.max(min, Math.round(value)));
diff --git a/src/services/operator-dashboard.ts b/src/services/operator-dashboard.ts
new file mode 100644
index 0000000000..3c37a2ff4b
--- /dev/null
+++ b/src/services/operator-dashboard.ts
@@ -0,0 +1,186 @@
+import {
+ countActiveAuthSessions,
+ countActiveDigestSubscriptions,
+ getCommandUsefulnessSummary,
+ getLatestScoringModelSnapshot,
+ getProductUsageRollupStatus,
+ listInstallationHealth,
+ listInstallations,
+ listLatestGitHubRateLimitObservations,
+ listProductUsageDailyRollups,
+ listRepositories,
+ summarizeMcpCompatibilityAdoption,
+ summarizeProductUsageEvents,
+} from "../db/repositories";
+import { getLatestRegistrySnapshot } from "../registry/sync";
+import type {
+ CommandUsefulnessSummary,
+ InstallationHealthRecord,
+ McpCompatibilityAdoptionSummary,
+ ProductUsageDailyRollupRecord,
+ ProductUsageRollupStatus,
+ ProductUsageSummary,
+ RegistrySnapshot,
+ RepositoryRecord,
+ ScoringModelSnapshotRecord,
+ WeeklyValueReport,
+} from "../types";
+import { loadUpstreamStatus, type UpstreamStatus } from "../upstream/ruleset";
+import { nowIso } from "../utils/json";
+import { buildWeeklyValueReport } from "./weekly-value-report";
+
+export type OperatorDashboardMetric = {
+ label: string;
+ value: string;
+ delta: string;
+};
+
+export type OperatorDashboardNoiseMetric = {
+ label: string;
+ value: number;
+ spark: number[];
+};
+
+export type OperatorDashboardPayload = {
+ generatedAt: string;
+ metrics: OperatorDashboardMetric[];
+ noiseReduction: OperatorDashboardNoiseMetric[];
+ weeklyReport: string[];
+ weeklyValueReport: WeeklyValueReport;
+ usageSummary: ProductUsageSummary;
+ usageRollups: ProductUsageDailyRollupRecord[];
+ usageRollupStatus: ProductUsageRollupStatus;
+ mcpCompatibilityAdoption: McpCompatibilityAdoptionSummary;
+ commandUsefulness: CommandUsefulnessSummary;
+ registry: RegistrySnapshot | null;
+ scoringModel: ScoringModelSnapshotRecord | null;
+ upstreamDrift: UpstreamStatus;
+};
+
+const USAGE_WINDOW_DAYS = 7;
+
+export async function buildOperatorDashboardPayload(env: Env): Promise {
+ const usageSince = new Date(Date.now() - USAGE_WINDOW_DAYS * 24 * 60 * 60 * 1000).toISOString();
+ const [
+ repositories,
+ installations,
+ health,
+ registry,
+ scoring,
+ upstreamDrift,
+ activeSessions,
+ digestSubscriptions,
+ rateLimits,
+ usageSummary,
+ usageRollups,
+ usageRollupStatus,
+ mcpCompatibilityAdoption,
+ commandUsefulness,
+ ] = await Promise.all([
+ listRepositories(env),
+ listInstallations(env),
+ listInstallationHealth(env),
+ getLatestRegistrySnapshot(env),
+ getLatestScoringModelSnapshot(env),
+ loadUpstreamStatus(env),
+ countActiveAuthSessions(env),
+ countActiveDigestSubscriptions(env),
+ listLatestGitHubRateLimitObservations(env, 20),
+ summarizeProductUsageEvents(env, usageSince),
+ listProductUsageDailyRollups(env, { limit: 14 }),
+ getProductUsageRollupStatus(env),
+ summarizeMcpCompatibilityAdoption(env, usageSince),
+ getCommandUsefulnessSummary(env),
+ ]);
+ const weeklyValueReport = buildWeeklyValueReport({
+ generatedAt: nowIso(),
+ variant: "operator",
+ days: USAGE_WINDOW_DAYS,
+ repositories,
+ installations,
+ health,
+ registry,
+ scoring,
+ upstreamDrift,
+ usageSummary,
+ usageRollups,
+ usageRollupStatus,
+ activeSessions,
+ digestSubscriptions,
+ });
+ const installedRepos = repositories.filter((repo: RepositoryRecord) => repo.isInstalled).length;
+ const registeredRepos = repositories.filter((repo: RepositoryRecord) => repo.isRegistered).length;
+ return {
+ generatedAt: nowIso(),
+ metrics: [
+ { label: "Active sessions", value: String(activeSessions), delta: "browser + CLI/MCP" },
+ { label: "Installations", value: String(installations.length), delta: `${installedRepos} installed repos` },
+ { label: "Registered repos", value: String(registeredRepos), delta: registry ? `${registry.repoCount} in latest registry` : "registry missing" },
+ { label: "Digest subscriptions", value: String(digestSubscriptions), delta: "store-only" },
+ { label: "Product events", value: String(usageSummary.totalEvents), delta: "last 7 days" },
+ { label: "Active users", value: String(usageSummary.activeActors), delta: "hashed, last 7 days" },
+ { label: "Activation rollups", value: usageRollupStatus.status, delta: usageRollupStatus.latestRollupDay ?? "not generated" },
+ {
+ label: "MCP stale clients",
+ value: String(mcpCompatibilityAdoption.staleEvents + mcpCompatibilityAdoption.incompatibleEvents),
+ delta: `${mcpCompatibilityAdoption.totalEvents} MCP event(s)`,
+ },
+ {
+ label: "Command usefulness",
+ value: `${commandUsefulness.totals.usefulCount}/${commandUsefulness.totals.feedbackCount}`,
+ delta: usefulnessDelta(commandUsefulness.totals.usefulnessRate),
+ },
+ {
+ label: "Install issues",
+ value: String(health.filter((record: InstallationHealthRecord) => record.status !== "healthy").length),
+ delta: "current health cache",
+ },
+ { label: "Rate-limit events", value: String(rateLimits.length), delta: "latest observations" },
+ ],
+ noiseReduction: [
+ {
+ label: "Healthy installations",
+ value: health.filter((record: InstallationHealthRecord) => record.status === "healthy").length,
+ spark: sparklineFromCounts(
+ health.filter((record: InstallationHealthRecord) => record.status === "healthy").length,
+ Math.max(health.length, 1),
+ ),
+ },
+ {
+ label: "Registered coverage",
+ value: registeredRepos,
+ spark: sparklineFromCounts(registeredRepos, Math.max(repositories.length, 1)),
+ },
+ {
+ label: "Installed coverage",
+ value: installedRepos,
+ spark: sparklineFromCounts(installedRepos, Math.max(repositories.length, 1)),
+ },
+ ],
+ weeklyReport: weeklyValueReport.summary,
+ weeklyValueReport,
+ usageSummary,
+ usageRollups,
+ usageRollupStatus,
+ mcpCompatibilityAdoption,
+ commandUsefulness,
+ registry,
+ scoringModel: scoring,
+ upstreamDrift,
+ };
+}
+
+export function latestUsageRollup(rollups: ProductUsageDailyRollupRecord[]): ProductUsageDailyRollupRecord | null {
+ if (rollups.length === 0) return null;
+ return [...rollups].sort((a, b) => b.day.localeCompare(a.day))[0] ?? null;
+}
+
+function usefulnessDelta(rate: number | null): string {
+ return rate === null ? "no feedback yet" : `${Math.round(rate * 100)}% useful over 30 days`;
+}
+
+function sparklineFromCounts(value: number, total: number): number[] {
+ const safeTotal = Math.max(total, 1);
+ const ratio = Math.min(1, Math.max(0, value / safeTotal));
+ return [Math.round(ratio * 40), Math.round(ratio * 55), Math.round(ratio * 70), Math.round(ratio * 85), Math.round(ratio * 100)];
+}
diff --git a/test/unit/operator-dashboard.test.ts b/test/unit/operator-dashboard.test.ts
new file mode 100644
index 0000000000..532e37cf61
--- /dev/null
+++ b/test/unit/operator-dashboard.test.ts
@@ -0,0 +1,85 @@
+import { describe, expect, it } from "vitest";
+import { buildOperatorDashboardPayload, latestUsageRollup } from "../../src/services/operator-dashboard";
+import type { ProductUsageDailyRollupRecord } from "../../src/types";
+import { createTestEnv } from "../helpers/d1";
+
+const FORBIDDEN_EXPORT_TERMS =
+ /wallet|hotkey|raw trust|trust[-\s]?score|payout|reward[-\s]?estimate|farming|private[-\s]?reviewability|public[-\s]?score[-\s]?(?:estimate|prediction)|\/Users|github_pat|ghp_/i;
+
+describe("operator dashboard payload", () => {
+ it("builds operator metrics from product usage rollups without sensitive strings", async () => {
+ const env = createTestEnv({ PRODUCT_USAGE_HASH_SALT: "operator-dashboard-test-salt" });
+ const payload = await buildOperatorDashboardPayload(env);
+ const serialized = JSON.stringify(payload);
+
+ expect(payload.metrics).toEqual(
+ expect.arrayContaining([
+ expect.objectContaining({ label: "Product events" }),
+ expect.objectContaining({ label: "Command usefulness" }),
+ expect.objectContaining({ label: "Activation rollups" }),
+ ]),
+ );
+ expect(payload.weeklyValueReport.variant).toBe("operator");
+ expect(payload.usageSummary).toMatchObject({ totalEvents: expect.any(Number), activeActors: expect.any(Number) });
+ expect(payload.commandUsefulness.totals).toMatchObject({ feedbackCount: expect.any(Number) });
+ expect(serialized).not.toMatch(FORBIDDEN_EXPORT_TERMS);
+ });
+
+ it("picks the newest rollup day for adoption insights", () => {
+ const rollups: ProductUsageDailyRollupRecord[] = [
+ rollup("2026-05-28"),
+ rollup("2026-05-30"),
+ rollup("2026-05-29"),
+ ];
+ expect(latestUsageRollup(rollups)?.day).toBe("2026-05-30");
+ expect(latestUsageRollup([])).toBeNull();
+ });
+});
+
+function rollup(day: string): ProductUsageDailyRollupRecord {
+ return {
+ day,
+ status: "complete",
+ totalEvents: 1,
+ activeActors: 1,
+ activeSessions: 1,
+ activeRepos: 1,
+ sourceEventCount: 1,
+ maxEventCapacity: 1000,
+ bySurface: [],
+ byOutcome: [],
+ byEvent: [],
+ byRepo: [],
+ byCommand: [],
+ byTool: [],
+ byRouteClass: [],
+ activation: {
+ loginActors: 1,
+ doctorPassActors: 1,
+ firstUsefulActionActors: 1,
+ fullyActivatedActors: 1,
+ githubInstalledRepos: 1,
+ githubFirstCommandRepos: 1,
+ githubUsefulMaintainerRepos: 1,
+ githubActivatedRepos: 1,
+ },
+ byRole: [{ role: "miner", count: 1, activeActors: 1, activeRepos: 0 }],
+ activationByRole: [
+ {
+ role: "miner",
+ loginActors: 1,
+ doctorPassActors: 1,
+ firstUsefulActionActors: 1,
+ fullyActivatedActors: 1,
+ githubInstalledRepos: 0,
+ githubFirstCommandRepos: 0,
+ githubUsefulMaintainerRepos: 0,
+ githubActivatedRepos: 0,
+ },
+ ],
+ activationBySurface: [],
+ retention: [],
+ generatedAt: "2026-06-01T00:00:00.000Z",
+ updatedAt: "2026-06-01T00:00:00.000Z",
+ };
+}