From b631d91629925928d5cd4de23beeb57baff351b0 Mon Sep 17 00:00:00 2001 From: JSONbored <49853598+JSONbored@users.noreply.github.com> Date: Thu, 25 Jun 2026 06:32:05 -0700 Subject: [PATCH] fix(ai-slop): resolve the shared neuron budget like the AI review path (default 10M, clamp 10M) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit runGittensoryAiSlopAdvisory read the SAME shared AI_DAILY_NEURON_BUDGET as the review path but computed `clampNumber(Number(env.AI_DAILY_NEURON_BUDGET || 10000), 0, 1_000_000)` — a 10k default + a 1M ceiling. ai-review.ts was deliberately fixed to default 10M / clamp 10M (a prod incident: the old 10k default "silently starves every dual-AI review into quota_exceeded"), but ai-slop kept the old misconfig. Since both features sum into ONE usage counter (sumAiEstimatedNeuronsSince), slop AI was starved well under the real shared budget. Resolve it identically to ai-review. --- src/services/ai-slop.ts | 6 +++++- test/unit/ai-slop.test.ts | 20 ++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/services/ai-slop.ts b/src/services/ai-slop.ts index 4932bf0b1d..f2f3035dca 100644 --- a/src/services/ai-slop.ts +++ b/src/services/ai-slop.ts @@ -190,7 +190,11 @@ export async function runGittensoryAiSlopAdvisory(env: Env, input: AiSlopInput): // the daily neuron budget. const freeCalls = input.providerKey ? 0 : WORKERS_SLOP_MAX_CALLS; const estimatedNeurons = freeCalls === 0 ? 0 : estimateNeurons(SLOP_SYSTEM_PROMPT.length + user.length, maxTokens, freeCalls); - const budget = clampNumber(Number(env.AI_DAILY_NEURON_BUDGET || 10000), 0, 1_000_000); + // Resolve the shared daily neuron budget IDENTICALLY to the AI review path (ai-review.ts): default HIGH + // (10,000,000) and clamp to 10,000,000 — both features sum into ONE usage counter (sumAiEstimatedNeuronsSince), + // so the old 10k default + 1M ceiling here starved slop AI into quota_exceeded well under the real shared budget. + const rawNeuronBudget = Number(env.AI_DAILY_NEURON_BUDGET); + const budget = clampNumber(env.AI_DAILY_NEURON_BUDGET && Number.isFinite(rawNeuronBudget) ? rawNeuronBudget : 10_000_000, 0, 10_000_000); const used = await sumAiEstimatedNeuronsSince(env, utcDayStartIso()); const remainingBudget = Math.max(0, budget - used); if (estimatedNeurons > remainingBudget) { diff --git a/test/unit/ai-slop.test.ts b/test/unit/ai-slop.test.ts index 9a02f9e62f..19bd074eee 100644 --- a/test/unit/ai-slop.test.ts +++ b/test/unit/ai-slop.test.ts @@ -169,6 +169,26 @@ describe("runGittensoryAiSlopAdvisory gating + fail-safe", () => { expect(result.estimatedNeurons).toBeGreaterThan(result.remainingBudget); }); + it("uses the full 10M shared budget, not the old 1M ceiling — slop survives heavy review spend (#review-audit)", async () => { + const run = vi.fn(async () => ({ response: "not json" })); + const env = createTestEnv({ AI: { run } as unknown as Ai, AI_SUMMARIES_ENABLED: "true", AI_PUBLIC_COMMENTS_ENABLED: "true", AI_DAILY_NEURON_BUDGET: "2000000" }); + // Prior shared spend of 1.5M neurons — OVER the old 1M ceiling, well under the 2M shared budget. + await recordAiUsageEvent(env, { feature: "ai_review", model: "m", status: "ok", estimatedNeurons: 1_500_000 }); + const result = await runGittensoryAiSlopAdvisory(env, baseInput); + expect(result.status).not.toBe("quota_exceeded"); // the old clamp(2M, 0, 1M) = 1M budget → quota_exceeded at 1.5M used + expect(run).toHaveBeenCalled(); + }); + + it("defaults the budget HIGH (10M) when AI_DAILY_NEURON_BUDGET is unset/invalid — no 10k starvation (#review-audit)", async () => { + const run = vi.fn(async () => ({ response: "not json" })); + const env = createTestEnv({ AI: { run } as unknown as Ai, AI_SUMMARIES_ENABLED: "true", AI_PUBLIC_COMMENTS_ENABLED: "true", AI_DAILY_NEURON_BUDGET: "" }); + // 2M prior spend — OVER the old 10k default, under the 10M default the fix uses. + await recordAiUsageEvent(env, { feature: "ai_review", model: "m", status: "ok", estimatedNeurons: 2_000_000 }); + const result = await runGittensoryAiSlopAdvisory(env, baseInput); + expect(result.status).not.toBe("quota_exceeded"); // the old `|| 10000` default → quota_exceeded at 2M used + expect(run).toHaveBeenCalled(); + }); + it("records the pre-budgeted retry and fallback estimate when all Workers AI outputs are unusable", async () => { const run = vi.fn(async () => ({ response: "not json" })); const env = enabledEnv(run);