test(evals): preserve announcement original response clock

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-11-04 16:43:52 -06:00
1 parent c6cc59282a
commit 34d748d06f
4 files changed
+60 -3

No files matched your search

@@ -40,6 +40,7 @@ const originalClockExecutionIds = new Set([
"chat-features.runner-opencode.local.navigation-preferences.quick-search",
"chat-features.runner-opencode.local.navigation-preferences.edit-profile",
"chat-features.runner-opencode.local.navigation-preferences.broken-link",
"chat-features.runner-opencode.local.navigation-preferences.dismiss-announcement",
"chat-features.runner-opencode.local.projects.share-project",
"chat-features.runner-opencode.local.access.cli-authorization",
"chat-features.runner-opencode.local.access.other-company",
@@ -3587,7 +3587,7 @@
"../../ui/src/pages/Projects.tsx",
"../../ui/src/components/AnnouncementWell.tsx"
],
"oracleVersion": "paperclip.chat-catalog.announcement-dismissal.v2",
"oracleVersion": "paperclip.chat-catalog.announcement-dismissal.v3",
"expectedRunCount": 1,
"turnTimeoutMs": 120000,
"attemptTimeoutMs": 300000,
@@ -0,0 +1,56 @@
import { afterEach, expect, it, vi } from "vitest";
import type { ChatCatalogContext } from "../chat-catalog-contract.js";
import { navigationBoundaryIdentityCases } from "./identity-navigation-boundaries.js";
const definition = navigationBoundaryIdentityCases.find(row => row.scenarioId === "navigation-preferences.dismiss-announcement")!;
const boundary = new Error("First actual publication read; no human action performed");
function fixture(replyAt = 151_600, deadline: number | undefined = 300_000) {
const read = vi.fn().mockRejectedValue(boundary);
const ctx = { observationDeadlineAt: deadline,
input: { execution: { id: "chat-features.runner-opencode.local.navigation-preferences.dismiss-announcement" },
page: { url: () => "http://fixture.invalid/ACO/chats/ceo" }, api: { get: read } },
refreshIssue: async () => {},
issue: () => ({ conversationState: Date.now() >= replyAt ? "waiting" : "active" }),
allRuns: async () => [{ id: "native", status: Date.now() >= replyAt ? "succeeded" : "running" }],
} as unknown as ChatCatalogContext;
return { ctx, read };
}
afterEach(() => vi.useRealTimers());
it("actual announcement act admits the native reply after151.6s within its original clock before human dismissal", async () => {
vi.useFakeTimers(); vi.setSystemTime(0);
const { ctx, read } = fixture();
const pending = definition.act!(ctx, { publication: { id: "publication" } }).catch(error => error);
await vi.advanceTimersByTimeAsync(151_000); expect(read).not.toHaveBeenCalled();
await vi.advanceTimersByTimeAsync(1000); expect(await pending).toBe(boundary);
expect(read).toHaveBeenCalledExactlyOnceWith("/api/announcements/current");
expect(definition.attemptTimeoutMs).toBe(300_000);
});
it("consumes elapsed setup instead of granting a fresh wait budget", async () => {
vi.useFakeTimers(); vi.setSystemTime(200_000);
const { ctx, read } = fixture(280_000, 270_000);
const pending = definition.act!(ctx, {}).catch(error => error);
await vi.advanceTimersByTimeAsync(70_500);
expect(String(await pending)).toContain("Timed out waiting for initial identity response");
expect(read).not.toHaveBeenCalled();
});
it.each(["failed", "cancelled", "timed_out"])("does not turn a %s native run into a human dismissal", async status => {
vi.useFakeTimers(); vi.setSystemTime(0);
const { ctx, read } = fixture(); ctx.allRuns = async () => [{ status }] as never;
await expect(definition.act!(ctx, {})).rejects.toThrow("Initial response failed");
expect(read).not.toHaveBeenCalled();
});
it.each([undefined, NaN, Infinity, 200_000, 199_999])("rejects missing or exhausted original deadline %s before observing", async deadline => {
vi.useFakeTimers(); vi.setSystemTime(200_000);
const { ctx, read } = fixture(0); ctx.observationDeadlineAt = deadline;
await expect(definition.act!(ctx, {})).rejects.toThrow("Original native observation budget exhausted or missing");
expect(read).not.toHaveBeenCalled();
});
it("rejects a successful observation that completes after the original deadline", async () => {
vi.useFakeTimers(); vi.setSystemTime(269_000);
const { ctx, read } = fixture(0, 270_000);
ctx.refreshIssue = async () => { vi.setSystemTime(270_001); };
const pending = definition.act!(ctx, {}).catch(error => error);
await vi.advanceTimersByTimeAsync(1500);
expect(String(await pending)).toContain("Timed out waiting for initial identity response");
expect(read).not.toHaveBeenCalled();
});
@@ -122,9 +122,9 @@ export const navigationBoundaryIdentityCases: readonly ChatCatalogCase[] = [
observerBeforePublicationId: observerPublication?.id, invalidAuditCompanyStatus };
} catch (error) { try { await observer.dispose(); } finally { observers.delete(ctx); const id = otherCompanies.get(ctx); if (id) await ctx.input.api.delete(`/api/companies/${id}`, { allowNotFound: true }); otherCompanies.delete(ctx); } throw error; }
}, gradeNavigationBoundary, {
sourceFiles: [...sources, "chat-announcement-fixture.ts", "harness-env.ts", "../../packages/shared/src/announcements.ts", "../../server/src/services/announcement-feed.ts", "../../server/src/routes/announcements.ts", "../../ui/src/pages/Projects.tsx", "../../ui/src/components/AnnouncementWell.tsx"], authentication: "human-session", oracleVersion: "paperclip.chat-catalog.announcement-dismissal.v2",
sourceFiles: [...sources, "chat-announcement-fixture.ts", "harness-env.ts", "../../packages/shared/src/announcements.ts", "../../server/src/services/announcement-feed.ts", "../../server/src/routes/announcements.ts", "../../ui/src/pages/Projects.tsx", "../../ui/src/components/AnnouncementWell.tsx"], authentication: "human-session", oracleVersion: "paperclip.chat-catalog.announcement-dismissal.v3",
async act(ctx, state) {
await waitForReply(ctx); const publication = state.publication as Publication; const source = ctx.input.page.url();
await waitForReply(ctx, chatCatalogOriginalPhaseDeadline(ctx)); const publication = state.publication as Publication; const source = ctx.input.page.url();
state.beforeHumanPublicationId = (await ctx.input.api.get<Publication | null>("/api/announcements/current"))?.id;
state.agentDismissStatus = await probeAgentPermission(ctx, `/api/announcements/${encodeURIComponent(publication.id)}/dismiss`, "POST", { companyId: ctx.input.fixtures.company.id });
const response = ctx.input.page.waitForResponse(row => row.request().method() === "POST" && new URL(row.url()).pathname === `/api/announcements/${publication.id}/dismiss`);