From febb47fb9268f617aff4aaff636f2f075a34b0f3 Mon Sep 17 00:00:00 2001 From: Dotta Date: Fri, 6 Nov 2026 05:37:36 -0600 Subject: [PATCH] fix(evals): await current skill review hydration within original budget Co-Authored-By: Paperclip --- .../integrator-skill-review-visible.test.ts | 23 +++++++++++++++++-- .../integrator-skill-update.ts | 9 +++++++- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/tests/runner-e2e/chat-catalog-domains/integrator-skill-review-visible.test.ts b/tests/runner-e2e/chat-catalog-domains/integrator-skill-review-visible.test.ts index daa0f6f1484..be87eade060 100644 --- a/tests/runner-e2e/chat-catalog-domains/integrator-skill-review-visible.test.ts +++ b/tests/runner-e2e/chat-catalog-domains/integrator-skill-review-visible.test.ts @@ -1,6 +1,6 @@ // @vitest-environment jsdom import { afterEach, beforeEach, expect, it, vi } from "vitest"; -import { readVisibleSkillReview } from "./integrator-skill-update.js"; +import { readVisibleSkillReview, waitForVisibleSkillReview } from "./integrator-skill-update.js"; const input = { runId: "run", humanCommentId: "human" }; const thread = (body: string) => `
Review before updating.
${body}
`; const response = '
Installed 0.4

Regression tests; split forms into smaller steps. No update applied yet.

'; @@ -10,7 +10,7 @@ beforeEach(() => { Object.defineProperty(HTMLElement.prototype, "innerText", { configurable: true, get() { return this.textContent ?? ""; } }); document.body.innerHTML = thread(response); }); -afterEach(() => vi.restoreAllMocks()); +afterEach(() => { vi.restoreAllMocks(); vi.useRealTimers(); }); it("reads rendered current completion only, excluding reasoning and tool output", () => { document.querySelector('[data-thread-anchor]')!.insertAdjacentHTML("afterbegin", '
PRIVATE REASONING
TOOL STDOUT
'); expect(readVisibleSkillReview(input)).toContain("Installed 0.4"); @@ -23,3 +23,22 @@ it("rejects hidden, foreign, old and ambiguously anchored conversation surfaces" document.body.innerHTML = thread(response).replace('comment-human', 'comment-foreign'); expect(readVisibleSkillReview(input)).toBe(""); document.body.innerHTML = thread(response) + thread(response); expect(readVisibleSkillReview(input)).toBe(""); }); + +it("waits for eventual current response hydration within the original deadline only", async () => { + vi.useFakeTimers(); vi.setSystemTime(1_000); + document.body.innerHTML = thread(""); + const evaluate = vi.fn(async () => readVisibleSkillReview(input)); + const ctx = { observationDeadlineAt: 2_000, input: { page: { evaluate } } } as unknown as Parameters[0]; + const pending = waitForVisibleSkillReview(ctx, input); + await vi.advanceTimersByTimeAsync(250); + document.body.innerHTML = thread(response); + await vi.advanceTimersByTimeAsync(250); + expect(await pending).toContain("Installed 0.4"); + expect(evaluate).toHaveBeenCalledTimes(3); + expect(ctx.observationDeadlineAt).toBe(2_000); + document.body.innerHTML = thread(response.replace('run:turn:0', 'foreign:turn:0')); + const timeout = expect(waitForVisibleSkillReview(ctx, input)).rejects.toThrow("Timed out waiting"); + await vi.advanceTimersByTimeAsync(500); await timeout; + expect(ctx.observationDeadlineAt).toBe(2_000); + await expect(waitForVisibleSkillReview(ctx, input)).rejects.toThrow("budget exhausted"); +}); diff --git a/tests/runner-e2e/chat-catalog-domains/integrator-skill-update.ts b/tests/runner-e2e/chat-catalog-domains/integrator-skill-update.ts index f53ef111d31..0a0399a3ef6 100644 --- a/tests/runner-e2e/chat-catalog-domains/integrator-skill-update.ts +++ b/tests/runner-e2e/chat-catalog-domains/integrator-skill-update.ts @@ -49,6 +49,13 @@ export function readVisibleSkillReview(input: { runId: string; humanCommentId: s }).flatMap(turn => [...turn.querySelectorAll('[data-testid="task-chat-final-response"]')] .filter(node => visible(node)).map(node => (node as HTMLElement).innerText)).join("\n"); } +export async function waitForVisibleSkillReview(ctx: ChatCatalogContext, input: { runId: string; humanCommentId: string }) { + remainingIdentityObservationBudget(ctx); + return pollUntil({ label: "current visible skill-review completion", deadlineAt: ctx.observationDeadlineAt!, intervalMs: 250, + load: () => ctx.input.page.evaluate(readVisibleSkillReview, input), + accept: text => Date.now() < ctx.observationDeadlineAt! && !!text.trim(), + }); +} export function skillReviewPreviewText(o: UpdateObservation) { const evidence = o.visibleReview, issueId = o.state.sourceIssueId; if (!evidence || typeof issueId !== "string") return o.reply; @@ -80,7 +87,7 @@ async function observeUpdate(ctx: ChatCatalogContext, state: Record