diff --git a/src/mcp/server.ts b/src/mcp/server.ts index ec60cea129..e55e8d46ff 100644 --- a/src/mcp/server.ts +++ b/src/mcp/server.ts @@ -907,7 +907,7 @@ export class GittensoryMcp { server.registerTool( "gittensory_get_burden_forecast", { - description: "Return the cached or freshly-computed maintainer burden forecast for a repo, including projected review load, queue growth risk, stale PR signals, and a freshness marker.", + description: "Return the cached maintainer burden forecast for a repo, including projected review load, queue growth risk, stale PR signals, and a freshness marker.", inputSchema: ownerRepoShape, outputSchema: freshnessResponseOutputSchema, }, @@ -1577,10 +1577,7 @@ export class GittensoryMcp { }; } return { - summary: - response.source === "snapshot" - ? `Gittensory burden forecast for ${fullName} (cached, ${response.freshness}).` - : `Gittensory burden forecast for ${fullName} (computed from cached metadata).`, + summary: `Gittensory burden forecast for ${fullName} (cached, ${response.freshness}).`, data: response as unknown as Record, }; } diff --git a/src/services/burden-forecast.ts b/src/services/burden-forecast.ts index 659373f02d..c4f850b3cf 100644 --- a/src/services/burden-forecast.ts +++ b/src/services/burden-forecast.ts @@ -1,11 +1,5 @@ -import { - getBurdenForecast, - getRepository, - listIssueSignalSample, - listOpenPullRequests, - listRecentMergedPullRequests, -} from "../db/repositories"; -import { buildBurdenForecast, buildCollisionReport, type BurdenForecast } from "../signals/engine"; +import { getBurdenForecast, getRepository } from "../db/repositories"; +import type { BurdenForecast } from "../signals/engine"; export const BURDEN_FORECAST_MAX_AGE_MS = 6 * 60 * 60 * 1000; @@ -39,22 +33,7 @@ export async function loadOrComputeBurdenForecastResponse(env: Env, fullName: st report: cached.payload as unknown as BurdenForecast, }; } - const [issues, pullRequests, recentMergedPullRequests] = await Promise.all([ - listIssueSignalSample(env, repoFullName), - listOpenPullRequests(env, repoFullName), - listRecentMergedPullRequests(env, repoFullName), - ]); - const collisions = buildCollisionReport(repoFullName, issues, pullRequests, recentMergedPullRequests); - const report = buildBurdenForecast(repo, issues, pullRequests, collisions, 30); - return { - status: "ready", - source: "computed", - repoFullName, - generatedAt: report.generatedAt, - ageSeconds: 0, - freshness: "fresh", - report, - }; + return null; } function forecastAgeMs(generatedAt: string): number { diff --git a/test/integration/api.test.ts b/test/integration/api.test.ts index c98a0d0d1d..8a550bb1d1 100644 --- a/test/integration/api.test.ts +++ b/test/integration/api.test.ts @@ -1718,13 +1718,12 @@ describe("api routes", () => { expect(snapshotIntelligenceBody.burdenForecastFreshness?.ageSeconds).toBeLessThan(Math.floor((BURDEN_FORECAST_MAX_AGE_MS + 120_000) / 1000)); await upsertRepositoryFromGitHub(env, { name: "uncached-burden", full_name: "entrius/uncached-burden", private: false, owner: { login: "entrius" }, default_branch: "main" }); - const computedIntelligence = await app.request("/v1/repos/entrius/uncached-burden/intelligence", { headers: apiHeaders(env) }, env); - expect(computedIntelligence.status).toBe(200); - await expect(computedIntelligence.json()).resolves.toMatchObject({ - source: "computed", - burdenForecast: { repoFullName: "entrius/uncached-burden", level: "low" }, - burdenForecastFreshness: { source: "computed", freshness: "fresh", ageSeconds: 0 }, - }); + const uncachedIntelligence = await app.request("/v1/repos/entrius/uncached-burden/intelligence", { headers: apiHeaders(env) }, env); + expect(uncachedIntelligence.status).toBe(200); + const uncachedIntelligenceBody = (await uncachedIntelligence.json()) as Record; + expect(uncachedIntelligenceBody).toMatchObject({ source: "computed" }); + expect(uncachedIntelligenceBody.burdenForecast).toBeUndefined(); + expect(uncachedIntelligenceBody.burdenForecastFreshness).toBeUndefined(); const degradedForecastEnv = withBurdenForecastReadFailure(env); const degradedIntelligence = await app.request("/v1/repos/entrius/allways-ui/intelligence", { headers: apiHeaders(env) }, degradedForecastEnv); @@ -5049,26 +5048,20 @@ describe("api routes", () => { }, }); - await upsertRepositoryFromGitHub(env, { name: "mcp-computed-burden", full_name: "entrius/mcp-computed-burden", private: false, owner: { login: "entrius" }, default_branch: "main" }); - const computedBurdenForecast = await app.request( + await upsertRepositoryFromGitHub(env, { name: "mcp-uncached-burden", full_name: "entrius/mcp-uncached-burden", private: false, owner: { login: "entrius" }, default_branch: "main" }); + const uncachedBurdenForecast = await app.request( "/mcp", { method: "POST", headers: mcpHeaders(env), - body: JSON.stringify({ jsonrpc: "2.0", id: "computed-burden", method: "tools/call", params: { name: "gittensory_get_burden_forecast", arguments: { owner: "entrius", repo: "mcp-computed-burden" } } }), + body: JSON.stringify({ jsonrpc: "2.0", id: "uncached-burden", method: "tools/call", params: { name: "gittensory_get_burden_forecast", arguments: { owner: "entrius", repo: "mcp-uncached-burden" } } }), }, env, ); - expect(computedBurdenForecast.status).toBe(200); - await expect(mcpJson(computedBurdenForecast)).resolves.toMatchObject({ + expect(uncachedBurdenForecast.status).toBe(200); + await expect(mcpJson(uncachedBurdenForecast)).resolves.toMatchObject({ result: { - structuredContent: { - status: "ready", - source: "computed", - repoFullName: "entrius/mcp-computed-burden", - freshness: "fresh", - report: { repoFullName: "entrius/mcp-computed-burden", level: "low" }, - }, + structuredContent: { status: "not_found", repoFullName: "entrius/mcp-uncached-burden" }, }, }); diff --git a/test/unit/burden-forecast.test.ts b/test/unit/burden-forecast.test.ts index 4cebe4257f..54b60fbed8 100644 --- a/test/unit/burden-forecast.test.ts +++ b/test/unit/burden-forecast.test.ts @@ -146,17 +146,13 @@ describe("loadOrComputeBurdenForecastResponse", () => { expect(response?.ageSeconds).toBe(Number.POSITIVE_INFINITY); }); - it("falls back to a computed forecast when no snapshot exists but the repo is known", async () => { + it("returns null when no snapshot exists for a known repo", async () => { const env = createTestEnv(); await upsertRepositoryFromGitHub(env, { name: "uncached", full_name: "owner/uncached", private: false, owner: { login: "owner" }, default_branch: "main" }); + const response = await loadOrComputeBurdenForecastResponse(env, "owner/uncached"); - expect(response).toMatchObject({ - status: "ready", - source: "computed", - freshness: "fresh", - ageSeconds: 0, - }); - expect(response?.report).toMatchObject({ repoFullName: "owner/uncached", level: "low" }); + + expect(response).toBeNull(); }); it("does not call broad request-time listers when a cached forecast exists", async () => { @@ -180,7 +176,7 @@ describe("loadOrComputeBurdenForecastResponse", () => { } }); - it("uses only the bounded per-repo listers when computing a missing forecast", async () => { + it("does not call broad request-time listers when no cached forecast exists", async () => { const env = createTestEnv(); await upsertRepositoryFromGitHub(env, { name: "computed-perf", full_name: "owner/computed-perf", private: false, owner: { login: "owner" }, default_branch: "main" }); const repositoriesModule = await import("../../src/db/repositories"); @@ -192,10 +188,9 @@ describe("loadOrComputeBurdenForecastResponse", () => { const response = await loadOrComputeBurdenForecastResponse(env, "owner/computed-perf"); - expect(response).toMatchObject({ source: "computed", report: { repoFullName: "owner/computed-perf" } }); + expect(response).toBeNull(); for (const spy of spies) { - expect(spy).toHaveBeenCalledTimes(1); - expect(spy).toHaveBeenCalledWith(env, "owner/computed-perf"); + expect(spy).not.toHaveBeenCalled(); spy.mockRestore(); } });