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
50 changes: 50 additions & 0 deletions apps/gittensory-ui/public/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -10880,6 +10880,36 @@
]
}
},
"/v1/app/analytics/weekly-value-report": {
"get": {
"responses": {
"200": {
"description": "Live app API response",
"content": {
"application/json": {
"schema": {
"type": "object",
"additionalProperties": {
"nullable": true
}
}
}
}
},
"401": {
"description": "Unauthorized"
}
},
"security": [
{
"GittensoryBearer": []
},
{
"GittensorySessionCookie": []
}
]
}
},
"/v1/app/commands/preview": {
"post": {
"responses": {
Expand Down Expand Up @@ -11269,6 +11299,26 @@
]
}
},
"/v1/internal/jobs/generate-weekly-value-report": {
"post": {
"responses": {
"202": {
"description": "Internal job queued"
},
"401": {
"description": "Invalid internal token"
}
},
"security": [
{
"GittensoryBearer": []
},
{
"GittensorySessionCookie": []
}
]
}
},
"/v1/internal/jobs/repair-data-fidelity": {
"post": {
"responses": {
Expand Down
33 changes: 27 additions & 6 deletions apps/gittensory-ui/src/routes/app.operator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ type OperatorDashboardResponse = {
metrics: Array<{ label: string; value: string; delta: string }>;
noiseReduction: Array<{ label: string; value: number; spark: number[] }>;
weeklyReport: string[];
weeklyValueReport?: {
freshness: { status: string; latestRollupDay?: string | null };
warnings: string[];
metrics: Array<{ id: string; label: string; value: number; detail: string }>;
};
upstreamDrift?: { status?: string } | null;
};

Expand Down Expand Up @@ -88,18 +93,34 @@ function OperatorDashboard() {
<div className="rounded-token border border-border bg-transparent p-5">
<h2 className="font-display text-token-lg font-semibold">Weekly value report</h2>
<p className="mt-1 text-token-xs text-muted-foreground">
Server-generated summary across installations and registry state.
Rollup-backed summary across usage, maintenance, and drift signals.
</p>
{data.weeklyValueReport ? (
<div className="mt-3 flex flex-wrap gap-2">
<StatusPill
status={data.weeklyValueReport.warnings.length > 0 ? "degraded" : "ready"}
>
Rollups{" "}
{data.weeklyValueReport.freshness.latestRollupDay ??
data.weeklyValueReport.freshness.status}
</StatusPill>
<StatusPill status={data.upstreamDrift?.status === "current" ? "ready" : "warn"}>
Drift · {data.upstreamDrift?.status ?? "unknown"}
</StatusPill>
</div>
) : null}
<ul className="mt-4 space-y-2 text-token-sm text-foreground/90">
{data.weeklyReport.map((line) => (
<li key={line}>· {line}</li>
))}
</ul>
<div className="mt-4">
<StatusPill status={data.upstreamDrift?.status === "current" ? "ready" : "warn"}>
Drift · {data.upstreamDrift?.status ?? "unknown"}
</StatusPill>
</div>
{data.weeklyValueReport?.warnings.length ? (
<ul className="mt-4 space-y-1 text-token-xs text-muted-foreground">
{data.weeklyValueReport.warnings.slice(0, 3).map((warning) => (
<li key={warning}>· {warning}</li>
))}
</ul>
) : null}
</div>
</section>
</div>
Expand Down
67 changes: 44 additions & 23 deletions src/api/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ import {
LATEST_RECOMMENDED_MCP_VERSION,
MINIMUM_SUPPORTED_MCP_VERSION,
} from "../services/mcp-compatibility";
import { buildWeeklyValueReport, generateWeeklyValueReport, loadWeeklyValueReport } from "../services/weekly-value-report";
import { loadOrComputeIssueQualityResponse } from "../services/issue-quality";
import { loadOrComputeBurdenForecastResponse } from "../services/burden-forecast";
import {
Expand Down Expand Up @@ -161,10 +162,8 @@ import type {
JsonValue,
ProductUsageOutcome,
ProductUsageSurface,
RegistrySnapshot,
RepoSyncSegmentRecord,
RepositoryRecord,
ScoringModelSnapshotRecord,
} from "../types";
import { errorMessage, nowIso } from "../utils/json";

Expand Down Expand Up @@ -787,6 +786,22 @@ export function createApp() {
getProductUsageRollupStatus(c.env),
summarizeMcpCompatibilityAdoption(c.env, usageSince),
]);
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({
Expand All @@ -808,7 +823,8 @@ export function createApp() {
{ 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: buildOperatorWeeklyReport({ repositories, installations, health, registry, scoring, upstreamDrift }),
weeklyReport: weeklyValueReport.summary,
weeklyValueReport,
usageSummary,
usageRollups,
usageRollupStatus,
Expand All @@ -835,6 +851,15 @@ export function createApp() {
return c.json({ generatedAt: nowIso(), status, rollups });
});

app.get("/v1/app/analytics/weekly-value-report", async (c) => {
const variant = c.req.query("variant") === "operator" ? "operator" : "public";
const allowedRoles: ControlPanelRoleName[] = variant === "operator" ? ["operator"] : ["miner", "maintainer", "owner", "operator"];
const forbidden = await requireAppRole(c, allowedRoles);
if (forbidden) return forbidden;
const days = Math.max(1, Math.min(31, Number(c.req.query("days") ?? 7) || 7));
return c.json(await loadWeeklyValueReport(c.env, { variant, days }));
});

app.get("/v1/app/commands", async (c) =>
c.json({
generatedAt: nowIso(),
Expand Down Expand Up @@ -1758,6 +1783,15 @@ export function createApp() {
return c.json({ ok: true, status: "queued", day, days }, 202);
});

app.post("/v1/internal/jobs/generate-weekly-value-report", async (c) => {
const body = await c.req.json().catch(() => ({}));
const days = Number.isFinite(Number(body?.days)) ? Math.max(1, Math.min(31, Math.round(Number(body.days)))) : undefined;
const variant = body?.variant === "public" ? "public" : "operator";
const message: JobMessage = { type: "generate-weekly-value-report", requestedBy: "api", variant, ...(days === undefined ? {} : { days }) };
await c.env.JOBS.send(message);
return c.json({ ok: true, status: "queued", variant, days }, 202);
});

app.post("/v1/internal/jobs/repair-data-fidelity", async (c) => {
const message: JobMessage = { type: "repair-data-fidelity", requestedBy: "api" };
await c.env.JOBS.send(message);
Expand All @@ -1778,6 +1812,13 @@ export function createApp() {
return c.json(await rollupProductUsageDaily(c.env, { ...(day ? { day } : {}), ...(days === undefined ? {} : { days }) }));
});

app.post("/v1/internal/jobs/generate-weekly-value-report/run", async (c) => {
const body = await c.req.json().catch(() => ({}));
const days = Number.isFinite(Number(body?.days)) ? Math.max(1, Math.min(31, Math.round(Number(body.days)))) : undefined;
const variant = body?.variant === "public" ? "public" : "operator";
return c.json(await generateWeeklyValueReport(c.env, { variant, ...(days === undefined ? {} : { days }) }));
});

app.post("/v1/internal/jobs/refresh-installation-health/run", async (c) => {
return c.json(await refreshInstallationHealth(c.env));
});
Expand Down Expand Up @@ -2053,26 +2094,6 @@ function buildDigestItems(args: {
return items;
}

function buildOperatorWeeklyReport(args: {
repositories: RepositoryRecord[];
installations: Awaited<ReturnType<typeof listInstallations>>;
health: InstallationHealthRecord[];
registry: RegistrySnapshot | null;
scoring: ScoringModelSnapshotRecord | null;
upstreamDrift: Awaited<ReturnType<typeof loadUpstreamStatus>>;
}): string[] {
const registered = args.repositories.filter((repo) => repo.isRegistered).length;
const installed = args.repositories.filter((repo) => repo.isInstalled).length;
const unhealthy = args.health.filter((record) => record.status !== "healthy").length;
return [
`${registered} registered repos tracked; ${installed} have installation coverage in the local cache.`,
`${args.installations.length} GitHub App installation(s), ${unhealthy} needing attention.`,
args.registry ? `Latest registry snapshot has ${args.registry.repoCount} repos and ${args.registry.warnings.length} warning(s).` : "Registry snapshot is missing.",
args.scoring ? `Scoring model ${args.scoring.activeModel} is loaded from ${args.scoring.sourceKind}.` : "Scoring model snapshot is missing.",
`Upstream drift status is ${args.upstreamDrift.status}.`,
];
}

async function buildRepoIntelligenceResponse(env: Env, fullName: string) {
let burdenForecastError: unknown;
const [repo, snapshots, dataQuality, burdenForecast] = await Promise.all([
Expand Down
3 changes: 3 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ async function enqueueScheduledJobs(env: Env, controller: ScheduledController):
jobs.push({ type: "refresh-upstream-drift", requestedBy: "schedule" });
jobs.push({ type: "rollup-product-usage", requestedBy: "schedule", days: 7 });
}
if (isHourly && scheduledAt.getUTCDay() === 1 && hour === 12) {
jobs.push({ type: "generate-weekly-value-report", requestedBy: "schedule", variant: "operator", days: 7 });
}
if (isFullSyncWindow) {
jobs.push({ type: "generate-signal-snapshots", requestedBy: "schedule" });
jobs.push({ type: "build-burden-forecasts", requestedBy: "schedule" });
Expand Down
12 changes: 11 additions & 1 deletion src/openapi/spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -522,7 +522,16 @@ export function buildOpenApiSpec() {
401: { description: "Unauthorized" },
},
});
for (const path of ["/v1/app/roles", "/v1/app/miner-dashboard", "/v1/app/maintainer-dashboard", "/v1/app/operator-dashboard", "/v1/app/commands", "/v1/app/digest", "/v1/app/analytics/mcp-compatibility"]) {
for (const path of [
"/v1/app/roles",
"/v1/app/miner-dashboard",
"/v1/app/maintainer-dashboard",
"/v1/app/operator-dashboard",
"/v1/app/commands",
"/v1/app/digest",
"/v1/app/analytics/mcp-compatibility",
"/v1/app/analytics/weekly-value-report",
]) {
registry.registerPath({
method: "get",
path,
Expand Down Expand Up @@ -603,6 +612,7 @@ export function buildOpenApiSpec() {
"/v1/internal/jobs/build-contributor-decision-packs",
"/v1/internal/jobs/build-burden-forecasts",
"/v1/internal/jobs/generate-signal-snapshots",
"/v1/internal/jobs/generate-weekly-value-report",
"/v1/internal/jobs/repair-data-fidelity",
]) {
registry.registerPath({
Expand Down
4 changes: 4 additions & 0 deletions src/queue/processors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ import { getOrCreateScoringModelSnapshot, refreshScoringModelSnapshot } from "..
import { buildAndPersistContributorDecisionPack, loadDecisionPackSharedInputs } from "../services/decision-pack";
import { executeAgentRun, explainBlockersWithAgent, planNextWork, preflightBranchWithAgent, preparePrPacketWithAgent } from "../services/agent-orchestrator";
import { loadIssueQualityReportMap } from "../services/issue-quality";
import { generateWeeklyValueReport } from "../services/weekly-value-report";
import {
buildUpstreamRulesetSnapshot,
detectAndPersistUpstreamDrift,
Expand Down Expand Up @@ -211,6 +212,9 @@ export async function processJob(env: Env, message: JobMessage): Promise<void> {
case "rollup-product-usage":
await rollupProductUsageDaily(env, { ...(message.day ? { day: message.day } : {}), ...(message.days === undefined ? {} : { days: message.days }) });
return;
case "generate-weekly-value-report":
await generateWeeklyValueReport(env, { variant: message.variant ?? "operator", ...(message.days === undefined ? {} : { days: message.days }) });
return;
case "run-agent":
await executeAgentRun(env, message.runId);
return;
Expand Down
Loading