Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions src/api/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4748,11 +4748,18 @@ function buildDigestItems(args: {
rateLimits: Awaited<ReturnType<typeof listLatestGitHubRateLimitObservations>>;
}) {
const items: Array<{ kind: "summary" | "review-now" | "queue" | "drift" | "install"; title: string; detail: string; meta?: string }> = [];
// Lead with `installed` (repos this instance actually operates on) rather than `registered` (gittensor-subnet
// membership, an opt-in plugin -- see gittensor-wire.ts). "0 registered" is the normal, expected headline for
// any operator who hasn't opted into the gittensor plugin and must not read as broken (#5026).
const installed = args.repositories.filter((repo) => repo.isInstalled).length;
const registered = args.repositories.filter((repo) => repo.isRegistered).length;
items.push({
kind: "summary",
title: `${registered} registered repositories tracked`,
detail: `${args.repositories.length} repositories are present in the local LoopOver data cache.`,
title: `${installed} installed repositories tracked`,
detail:
registered > 0
? `${args.repositories.length} repositories are present in the local LoopOver data cache; ${registered} registered with the gittensor plugin.`
: `${args.repositories.length} repositories are present in the local LoopOver data cache.`,
meta: "registry",
});
const unhealthy = args.health.filter((record) => record.status !== "healthy");
Expand Down
8 changes: 7 additions & 1 deletion src/services/operator-dashboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,13 @@ export async function buildOperatorDashboardPayload(
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: "Registered repos",
value: String(registeredRepos),
// A null registry is the normal, expected state for any operator who hasn't opted into the
// gittensor plugin (see gittensor-wire.ts) -- "missing" reads as broken when it isn't (#5026).
delta: registry ? `${registry.repoCount} in latest registry` : "gittensor plugin not enabled",
},
{ label: "Digest subscriptions", value: String(digestSubscriptions), delta: "store-only" },
{ label: "Product events", value: String(usageSummary.totalEvents), delta: `last ${windowDays} days` },
{ label: "Active users", value: String(usageSummary.activeActors), delta: `hashed, last ${windowDays} days` },
Expand Down
68 changes: 67 additions & 1 deletion test/integration/api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,72 @@ describe("api routes", () => {
expect(legacyPerRepoDrift.status).toBe(404);
});

it("reads registered-repo copy as an honest, non-alarming state whether or not the gittensor plugin is opted into (#5026)", async () => {
const app = createApp();

// No registry snapshot ever persisted: the normal, expected state for an operator who hasn't opted
// into the gittensor plugin. "registered: 0" must not read as broken.
const unregisteredEnv = createTestEnv();
await upsertRepositoryFromGitHub(
unregisteredEnv,
{ name: "installed-only", full_name: "acme/installed-only", private: false, owner: { login: "acme" }, default_branch: "main" },
321,
);
const unregisteredOperator = await app.request("/v1/app/operator-dashboard", { headers: apiHeaders(unregisteredEnv) }, unregisteredEnv);
expect(unregisteredOperator.status).toBe(200);
await expect(unregisteredOperator.json()).resolves.toMatchObject({
metrics: expect.arrayContaining([
expect.objectContaining({ label: "Registered repos", value: "0", delta: "gittensor plugin not enabled" }),
]),
});
const unregisteredDigest = await app.request("/v1/app/digest", { headers: apiHeaders(unregisteredEnv) }, unregisteredEnv);
expect(unregisteredDigest.status).toBe(200);
await expect(unregisteredDigest.json()).resolves.toMatchObject({
items: expect.arrayContaining([
expect.objectContaining({
kind: "summary",
title: "1 installed repositories tracked",
detail: "1 repositories are present in the local LoopOver data cache.",
}),
]),
});

// A registry snapshot IS persisted (gittensor plugin opted in somewhere): both copy surfaces should
// report the registered count honestly instead of the unregistered-branch wording.
const registeredEnv = createTestEnv();
await upsertRepositoryFromGitHub(
registeredEnv,
{ name: "installed-and-registered", full_name: "acme/installed-and-registered", private: false, owner: { login: "acme" }, default_branch: "main" },
654,
);
await persistRegistrySnapshot(
registeredEnv,
normalizeRegistryPayload(
{ "acme/installed-and-registered": { emission_share: 0.01, issue_discovery_share: 0, label_multipliers: {}, trusted_label_pipeline: false } },
{ kind: "raw-github", url: "fixture://registered-registry" },
"2026-06-01T00:00:00.000Z",
),
);
const registeredOperator = await app.request("/v1/app/operator-dashboard", { headers: apiHeaders(registeredEnv) }, registeredEnv);
expect(registeredOperator.status).toBe(200);
await expect(registeredOperator.json()).resolves.toMatchObject({
metrics: expect.arrayContaining([
expect.objectContaining({ label: "Registered repos", value: "1", delta: "1 in latest registry" }),
]),
});
const registeredDigest = await app.request("/v1/app/digest", { headers: apiHeaders(registeredEnv) }, registeredEnv);
expect(registeredDigest.status).toBe(200);
await expect(registeredDigest.json()).resolves.toMatchObject({
items: expect.arrayContaining([
expect.objectContaining({
kind: "summary",
title: "1 installed repositories tracked",
detail: "1 repositories are present in the local LoopOver data cache; 1 registered with the gittensor plugin.",
}),
]),
});
});

it("serves upstream ruleset status, ruleset snapshots, and drift reports through private APIs", async () => {
const app = createApp();
const env = createTestEnv({ GITHUB_PUBLIC_TOKEN: "public-token" });
Expand Down Expand Up @@ -2351,7 +2417,7 @@ describe("api routes", () => {
const emptyOperator = await app.request("/v1/app/operator-dashboard", { headers: apiHeaders(emptyEnv) }, emptyEnv);
expect(emptyOperator.status).toBe(200);
await expect(emptyOperator.json()).resolves.toMatchObject({
metrics: expect.arrayContaining([expect.objectContaining({ label: "Registered repos", delta: "registry missing" })]),
metrics: expect.arrayContaining([expect.objectContaining({ label: "Registered repos", delta: "gittensor plugin not enabled" })]),
recommendationQuality: expect.objectContaining({
empty: true,
sparse: false,
Expand Down