Files
PaperClipAI/tests/runner-e2e/scheduled-forecast.test.ts
T
DottaandPaperclip 9b7d5b35eb fix(evals): refresh forecasts from complete request receipts (#17975)
## Thinking Path

> - Paperclip coordinates agent work through Runner.
> - Scheduled Product E2E campaigns need measured cost and time
forecasts.
> - The saved forecast samples came from October 21.
> - The October 27 qualifications retain final usage for all 19 model
requests.
> - The Claude measurements are hosted; the Codex measurements are
local.
> - This change refreshes the samples and keeps measurement hosts
separate.
> - Operators can inspect the latest evidence without treating it as
current qualification or an invoice.

## Linked Issues or Issue Description

Refs #17127 and #17946.

**What happened?**
The scheduled forecast still uses the older pilot amounts. It reports no
hosted measurement and does not record the measurement host per sample.

**What did you expect to happen?**
The forecast must retain the latest complete model receipts and their
source hashes. It must keep local and hosted timing cohorts separate. A
changed definition must remain unqualified.

**Steps to reproduce**
Prepare the daily or weekly forecast from the default branch. Inspect
its saved measurement date, totals, and hosted fields.

## What Changed

- Refresh four samples from two intact request ledgers and four retained
Product results.
- Record measurement hosts, final request counts, and original campaign
outcomes.
- Add historical hosted totals. Current hosted totals still require
matching evidence for every selected cell.
- Keep hosts in separate per-cell cohorts. Preserve definition drift and
unknown slow-tail estimates.
- Document the model-cost scope and timing limits.

## Verification

- Forecast and scheduled-preflight tests: 33 passed.
- The extraction replays both hash-checked ledgers, requires final
receipts, and checks the retained pricecards. The four samples cover 19
final requests.
- Daily historical model subtotal: $0.428650100; summed attempt time:
107.286 seconds.
- Weekly historical model subtotal: $0.793676500; summed attempt time:
158.093 seconds. The Codex samples are local.
- Full Product support suite at the initial head: 4,510 passed, seven
failed, three skipped. A clean 6b9e5f3a baseline with the same installed
dependencies reproduces the same seven failures: two Copilot image
assertions, four workflow-selection shell assertions, and one database
startup test.
- Product typecheck also fails on that clean baseline because the shared
installed Runner workspace exports are stale. The fresh CI build remains
the typecheck gate.
- No provider request, new live campaign, budget change, or schedule
activation is part of this PR.

## Risks

- These are successful-attempt measurements, not a prediction of failure
frequency or a campaign invoice.
- The feedback source fingerprint changed after the samples.
Current-compatible totals remain unknown; no historical fingerprint is
rewritten.
- Hosted worker cases passed, but their original workflow failed during
report dependency setup. That outcome stays visible.
- Hosted detection deliberately accepts only the measured GitHub Ubuntu
environment. Other host labels remain unqualified until declared with
their own evidence.
- The weekly selection still has no complete hosted timing measurement.
Infrastructure, storage, and observability charges stay separate from
model spend.

## Model Used

OpenAI Codex, GPT-6 based, with tool execution and extended reasoning.
The exact serving model ID and context window are not exposed in this
session.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-27 23:13:08 -05:00

124 lines
7.1 KiB
TypeScript

import { expect, it } from "vitest";
import { runnerMatrix } from "./catalog.js";
import { scheduledForecast } from "./scheduled-forecast.js";
import { scheduledSelection } from "./scheduled-operations.js";
import measurements from "./scheduled-measurements.json" with { type: "json" };
import prices from "./scheduled-prices.json" with { type: "json" };
import type { EvalOperationsManifest } from "./operations.js";
const models = prices.models as EvalOperationsManifest["models"];
const selected = (cadence: "daily" | "weekly") => runnerMatrix.filter(cell => scheduledSelection(cadence).includes(cell.id));
it.each([
["daily", "0.428650100", 107286], ["weekly", "0.793676500", 158093],
] as const)("retains exact historical %s measurements without inventing tail estimates", (cadence, modelUsd, summedAttemptMs) => {
const forecast = scheduledForecast(selected(cadence), models);
expect(forecast.historicalSuccessfulSelection).toEqual({ modelUsd, summedAttemptMs });
expect(forecast.measurementSnapshotSha256).toMatch(/^[0-9a-f]{64}$/);
expect(forecast).toMatchObject({ hostedModelUsd: null, hostedAttemptMs: null, selectionP95: null, costStatus: "estimated" });
expect(forecast.historicalHostedSelection).toEqual(cadence === "daily" ? { modelUsd, summedAttemptMs } : { modelUsd: null, summedAttemptMs: null });
expect(forecast.compatibleSuccessfulSelection).toEqual({ modelUsd: null, summedAttemptMs: null });
for (const cell of forecast.cells) {
expect(cell.historicalSuccessful.samples).toBe(1);
expect(cell.historicalSuccessful.modelUsd.p95).toBeNull();
expect(cell.historicalSuccessful.attemptMs.p95).toBeNull();
expect(cell.samples[0]!.resultSha256).toMatch(/^[0-9a-f]{64}$/);
}
});
it("does not treat the older feedback definition as a current measurement", () => {
const forecast = scheduledForecast(selected("daily"), models);
const feedback = forecast.cells.find(cell => cell.executionId.startsWith("feedback-"))!;
expect(feedback.samples[0]!.mismatches).toContain("definition");
expect(feedback.compatibleSuccessful.samples).toBe(0);
expect(forecast.compatibleSuccessfulSelection).toEqual({ modelUsd: null, summedAttemptMs: null });
});
it.each(["definitionHash", "model", "environmentId", "runtimeMode", "route", "priceVersion"] as const)("excludes incompatible %s samples without dropping their evidence", field => {
const snapshot = structuredClone(measurements);
const cell = selected("daily").find(cell => cell.id.startsWith("core-"))!;
const sample = snapshot.samples.find(sample => sample.executionId === cell.id)!;
sample[field] = field === "definitionHash" ? "a".repeat(64) : "different";
const forecast = scheduledForecast([cell], models, snapshot);
expect(forecast.cells[0]!.compatibleSuccessful.samples).toBe(0);
expect(forecast.cells[0]!.samples).toHaveLength(1);
expect(forecast.compatibleSuccessfulSelection.modelUsd).toBeNull();
});
it("keeps missing cells and empty selections unknown", () => {
const snapshot = structuredClone(measurements);
snapshot.samples = [];
expect(scheduledForecast(selected("daily"), models, snapshot).historicalSuccessfulSelection)
.toEqual({ modelUsd: null, summedAttemptMs: null });
expect(scheduledForecast([], models).compatibleSuccessfulSelection)
.toEqual({ modelUsd: null, summedAttemptMs: null });
});
it("computes distributions within one cohort and does not sum P95s", () => {
const snapshot = structuredClone(measurements);
const cell = selected("daily").find(cell => cell.id.startsWith("core-"))!;
const sample = snapshot.samples.find(sample => sample.executionId === cell.id)!;
snapshot.samples = Array.from({ length: 20 }, (_, index) => ({ ...sample, attemptId: `attempt-${index}`,
knownModelUsd: String(index + 1), attemptDurationMs: (index + 1) * 1000 }));
const forecast = scheduledForecast([cell], models, snapshot);
expect(forecast.cells[0]!.compatibleSuccessful).toEqual({ samples: 20,
modelUsd: { mean: "10.500000000", median: "10.500000000", p95: "19.000000000" },
attemptMs: { mean: 10500, median: 10500, p95: 19000 } });
expect(forecast.selectionP95).toBeNull();
snapshot.samples.push({ ...sample, attemptId: "other-cohort", definitionHash: "a".repeat(64) });
const mixed = scheduledForecast([cell], models, snapshot);
expect(mixed.cells[0]!.historicalCohorts).toHaveLength(2);
expect(mixed.historicalSuccessfulSelection.modelUsd).toBeNull();
expect(mixed.cells[0]!.compatibleSuccessful.samples).toBe(20);
});
it("rejects duplicate artifacts and invalid measurement provenance", () => {
const snapshot = structuredClone(measurements);
snapshot.samples.push(snapshot.samples[0]!);
expect(() => scheduledForecast(selected("daily"), models, snapshot)).toThrow("Duplicate forecast attempt");
snapshot.samples.pop();
snapshot.samples[0]!.resultSha256 = "missing";
expect(() => scheduledForecast(selected("daily"), models, snapshot)).toThrow("provenance");
});
it("preserves nanodollar costs and genuine measured zero", () => {
const snapshot = structuredClone(measurements);
const cell = selected("daily").find(cell => cell.id.startsWith("core-"))!;
const sample = snapshot.samples.find(sample => sample.executionId === cell.id)!;
snapshot.samples = [{ ...sample, knownModelUsd: "0.000000001", attemptDurationMs: 0 }];
expect(scheduledForecast([cell], models, snapshot).compatibleSuccessfulSelection)
.toEqual({ modelUsd: "0.000000001", summedAttemptMs: 0 });
snapshot.samples[0]!.knownModelUsd = "0";
expect(scheduledForecast([cell], models, snapshot).compatibleSuccessfulSelection.modelUsd).toBe("0.000000000");
});
it("keeps hosts separate and requires hosted evidence for the whole selection", () => {
const snapshot = structuredClone(measurements);
const cell = selected("daily").find(cell => cell.id.startsWith("core-"))!;
const sample = snapshot.samples.find(sample => sample.executionId === cell.id)!;
snapshot.samples = [sample, { ...sample, attemptId: "different-host", measurementHost: "local-docker-linux-arm64" }];
const forecast = scheduledForecast([cell], models, snapshot);
expect(forecast.cells[0]!.historicalCohorts).toHaveLength(2);
expect(forecast.cells[0]!.compatibleSuccessful.samples).toBe(0);
expect(forecast.cells[0]!.samples).toHaveLength(2);
expect(forecast.hostedModelUsd).toBe(sample.knownModelUsd);
expect(scheduledForecast(selected("weekly"), models).hostedModelUsd).toBeNull();
});
it("uses current hosted totals only when every selected definition matches", () => {
const snapshot = structuredClone(measurements);
for (const cell of selected("daily")) snapshot.samples.find(sample => sample.executionId === cell.id)!.definitionHash = cell.suiteDefinitionHash;
const forecast = scheduledForecast(selected("daily"), models, snapshot);
expect(forecast.hostedModelUsd).toBe("0.428650100");
expect(forecast.hostedAttemptMs).toBe(107286);
});
it.each([
{ measurementHost: undefined }, { measurementHost: "" },
{ finalModelRequests: 0 }, { finalModelRequests: 1.5 },
])("rejects invalid host or final-receipt provenance %j", patch => {
const snapshot = structuredClone(measurements);
Object.assign(snapshot.samples[0]!, patch);
expect(() => scheduledForecast(selected("daily"), models, snapshot)).toThrow("provenance");
});