mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-27 23:21:53 +02:00
* fix(browse): prepare reliable cookie import wave for validation * ci: sequence quality and behavior for validation branch * fix(browse): isolate Windows qualification and preserve native diagnostics * test(browse): cover cookie workflow quality and isolate Windows user paths * test(browse): trace native member startup and initialize fresh folders * fix(browse): keep Windows member stdin alive through EOF * fix(browse): latch native timeouts and compare contained Edge startup * test(browse): verify native version metadata and actual Windows argv * test(browse): qualify Dia import on isolated macOS CI * fix(browse): require picker origin for session mutations * fix(browse): bound credential reads through stream completion * test(browse): inspect owned Windows process arguments natively * test(evals): preserve passing coverage during cookie repair reruns * test(browse): isolate Dia qualification in a fresh macOS account * test(browse): pass bounded integer timeouts to native Mac probes * test(browse): distinguish Windows profile initialization from containment * test(browse): await descendant pipe readiness before parent exit * test(browse): initialize and restore isolated macOS Keychain state * test(browse): initialize Windows fixture folders before qualification * test(ci): pin the same Node runtime across Windows checks * test(browse): distinguish native macOS browser preflight stages * test(browse): isolate Windows descendant console lifetime * test(browse): preserve native receipts and identify fixture lock holders * test(browse): prepare dependency resolution before native Mac worker startup * test(ci): include lock and close checks in native diagnostics * test(browse): preserve native owner probe stages and subprocess deadlines * fix(browse): classify Chromium profile-in-use exit precisely * test(browse): retain Mac qualification evidence through cleanup failures * test(browse): bound Mac fixture paths and retire its owned user domain * test(browse): accept vanished fixture entries without weakening cleanup * test(browse): identify probe-created macOS user domains safely * test(browse): observe Mac user domains without targeting them first * test(browse): use passive fresh-user ownership throughout Mac qualification * test(browse): distinguish profile and registered-home Keychain lookups * test(browse): qualify Dia under one registered account home * test(browse): identify Dia startup and owned process-group failures * test(browse): classify bounded Dia startup diagnostics without leaking output * fix(test): preserve native Mac sandboxing and reap owned browser children * fix(browse): preserve Chromium sandboxing for native profile imports * test(browse): inspect signed Mach-O architecture without launching Xcode tools * test(browse): sample pending Dia startup and reap on all cleanup paths * test(browse): compare protected Dia launches in fresh Bun and Node accounts * test(browse): inspect isolated Mac GUI readiness without browser access * v1.90.0.0 fix: bind cookie picker actions to their document * test: validate cookie guards and fit nested launch fixtures * ci: configure the bundled Chromium sandbox helper * fix(browse): classify Playwright authentication timeouts * test: retain bounded Windows lifecycle diagnostics * test(cso): reuse bounded NTFS precision candidates * test(review): handle explicit preservation choices safely * test(browse): remove owned fixture directories with explicit primitives * test(review): distinguish descriptive reuse from edit commitments * test: admit only the approved unscored cookie workflow refusal * test: keep the Office Hours judge mock export-complete * fix: keep dependency-free CI planners independent of the model SDK * test: observe the exact holder after a native fixture unlink failure * fix: start seeded PTY observations at owned readiness * test: acquire identity-bound Windows deletion admission before profile resets * test: preserve qualified Git index bits without authorizing mutations
334 lines
21 KiB
TypeScript
334 lines
21 KiB
TypeScript
import { afterEach, describe, expect, test } from 'bun:test';
|
|
import { createHash } from 'node:crypto';
|
|
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
import { tmpdir } from 'node:os';
|
|
import { join, resolve } from 'node:path';
|
|
import { buildCookieWorkflowJudgeInput, COOKIE_WORKFLOW_JUDGE } from './helpers/cookie-workflow-judge-input';
|
|
import { buildWorkflowJudgePrompt, readWorkflowJudgeInput } from './helpers/workflow-judge-input';
|
|
import { prepareWorkflowJudgeCache, type WorkflowCacheOptions } from './helpers/workflow-judge-cache';
|
|
import type { EvalTestEntry } from './helpers/eval-store';
|
|
import { selectTests } from './helpers/test-selection';
|
|
import { E2E_TOUCHFILES, LLM_JUDGE_TOUCHFILES } from './helpers/touchfiles-data';
|
|
import { selectPrProfile } from '../scripts/test-pr-profile';
|
|
import { JUDGE_MS } from './helpers/eval-budgets';
|
|
import { JudgeRefusalError, DEFAULT_JUDGE_MAX_TOKENS } from './helpers/llm-judge';
|
|
import { COOKIE_MANUAL_REVIEW_FILE, getCookieWorkflowManualReview, isManualReviewEntry } from './helpers/cookie-workflow-manual-review';
|
|
|
|
const ROOT = resolve(import.meta.dir, '..');
|
|
const NAME = 'setup-browser-cookies/SKILL.md workflow';
|
|
const scratch: string[] = [];
|
|
const skill = 'excluded preamble\n# Setup Browser Cookies\nRead the gstack root\'s `BROWSER.md`, **Choosing a source and checking sign-in**.\nExact body.\n';
|
|
const browser = 'excluded browser introduction\n#### Choosing a source and checking sign-in\nExact reference.\n\n### Tabs + frames\nexcluded tab commands\n';
|
|
|
|
function fixture(entry: string | null = skill, reference: string | null = browser): string {
|
|
const root = mkdtempSync(join(tmpdir(), 'gstack-cookie-judge-'));
|
|
scratch.push(root);
|
|
mkdirSync(join(root, 'setup-browser-cookies'));
|
|
if (entry !== null) writeFileSync(join(root, 'setup-browser-cookies/SKILL.md'), entry);
|
|
if (reference !== null) writeFileSync(join(root, 'BROWSER.md'), reference);
|
|
return root;
|
|
}
|
|
|
|
function approveFixture(root: string) {
|
|
const input = buildCookieWorkflowJudgeInput(root);
|
|
const approval = { ...JSON.parse(readFileSync(join(ROOT, COOKIE_MANUAL_REVIEW_FILE), 'utf8')),
|
|
prompt_sha256: input.sha256, prompt_bytes: Buffer.byteLength(input.prompt), model: 'fixture-model',
|
|
reason: 'Synthetic approval fixture, not live review evidence' };
|
|
mkdirSync(join(root, '.github'), { recursive: true });
|
|
writeFileSync(join(root, COOKIE_MANUAL_REVIEW_FILE), JSON.stringify(approval));
|
|
return approval;
|
|
}
|
|
|
|
const refusal = () => new JudgeRefusalError({ id: 'msg_synthetic', _request_id: 'req_synthetic',
|
|
model: 'fixture-model', usage: { input_tokens: 1, output_tokens: 0 }, content: [] });
|
|
|
|
afterEach(() => {
|
|
for (const root of scratch.splice(0)) rmSync(root, { recursive: true, force: true });
|
|
});
|
|
|
|
type FixtureScore = { clarity: number; completeness: number; actionability: number; reasoning: string };
|
|
const passingScore: FixtureScore = { ...COOKIE_WORKFLOW_JUDGE.thresholds, reasoning: 'Synthetic fixture score' };
|
|
|
|
function actualCookieCallback(root: string, overrides: {
|
|
judge?: () => Promise<FixtureScore>;
|
|
clock?: () => number;
|
|
budget?: number;
|
|
setTimer?: typeof setTimeout;
|
|
clearTimer?: typeof clearTimeout;
|
|
} = {}) {
|
|
const source = readFileSync(join(ROOT, 'test/skill-llm-eval.test.ts'), 'utf8');
|
|
const managedStart = source.indexOf('async function runWorkflowJudge');
|
|
const managedEnd = source.indexOf('// Block 1:', managedStart);
|
|
const start = source.indexOf("describeIfSelected('Cookie setup workflow quality'");
|
|
const end = source.indexOf('// Module-level afterAll', start);
|
|
expect(managedStart).toBeGreaterThan(-1);
|
|
expect(managedEnd).toBeGreaterThan(managedStart);
|
|
expect(start).toBeGreaterThan(-1);
|
|
expect(end).toBeGreaterThan(start);
|
|
const registration = new Bun.Transpiler({ loader: 'ts' }).transformSync(source.slice(managedStart, managedEnd) + source.slice(start, end));
|
|
const requests: Array<{ prompt: string; model: undefined; signal: AbortSignal }> = [];
|
|
const records: EvalTestEntry[] = [];
|
|
const attempts = new Map<string, { attempt: number }>();
|
|
let callback: () => Promise<void> = async () => { throw new Error('Judge callback was not registered'); };
|
|
new Function('describeIfSelected', 'testIfSelected', 'ROOT', 'buildCookieWorkflowJudgeInput', 'resolveEvalModel', 'callJudge', 'COOKIE_WORKFLOW_JUDGE', 'JUDGE_MS', 'WORKFLOW_JUDGE_TEST_MS', 'WORKFLOW_JUDGE_RECORD_MS', 'evalCollector', 'expect', 'console', 'readWorkflowJudgeInput', 'buildWorkflowJudgePrompt', 'prepareWorkflowJudgeCache', 'workflowJudgeAttempts', 'performance', 'setTimeout', 'clearTimeout', 'JudgeRefusalError', 'getCookieWorkflowManualReview', 'DEFAULT_JUDGE_MAX_TOKENS', registration)(
|
|
(_suite: string, names: string[], run: () => void) => { expect(names).toEqual([NAME]); run(); },
|
|
(name: string, run: () => Promise<void>, budget: number) => { expect(name).toBe(NAME); expect(budget).toBe(JUDGE_MS + 10_000); callback = run; },
|
|
root, buildCookieWorkflowJudgeInput, () => 'fixture-model',
|
|
async (prompt: string, model: undefined, options: { signal: AbortSignal }) => { requests.push({ prompt, model, signal: options.signal }); return overrides.judge ? overrides.judge() : passingScore; },
|
|
COOKIE_WORKFLOW_JUDGE, overrides.budget ?? JUDGE_MS, JUDGE_MS + 10_000, 5_000,
|
|
{ addTest: (record: EvalTestEntry) => records.push(record) }, expect, { log() {} },
|
|
readWorkflowJudgeInput, buildWorkflowJudgePrompt,
|
|
(options: WorkflowCacheOptions) => prepareWorkflowJudgeCache({ ...options, env: {
|
|
EVALS_CACHE_DIR: join(root, 'cache'), EVALS_CACHE_REPOSITORY: 'fixture/cookie', EVALS_CACHE_PR: '1',
|
|
EVALS_CACHE_RUNTIME_ID: 'a'.repeat(64), EVALS_TIER: 'gate', EVALS_RUN_ID: 'cookie-fixture',
|
|
} }),
|
|
attempts, overrides.clock ? { now: overrides.clock } : performance,
|
|
overrides.setTimer ?? setTimeout, overrides.clearTimer ?? clearTimeout,
|
|
JudgeRefusalError, getCookieWorkflowManualReview, DEFAULT_JUDGE_MAX_TOKENS,
|
|
);
|
|
return { run: () => callback(), requests, records, attempts };
|
|
}
|
|
|
|
describe('cookie workflow judge input', () => {
|
|
test('the registered callback accepts only an approved empty provider refusal, with no score or cache credit', async () => {
|
|
const root = fixture();
|
|
approveFixture(root);
|
|
const h = actualCookieCallback(root, { judge: async () => { throw refusal(); } });
|
|
await h.run();
|
|
expect(h.requests).toHaveLength(1);
|
|
expect(h.records).toHaveLength(1);
|
|
expect(h.records[0]).toMatchObject({ passed: false, execution: 'executed', exit_reason: 'provider_refusal' });
|
|
expect(isManualReviewEntry(h.records[0])).toBe(true);
|
|
expect(h.records[0]).not.toHaveProperty('judge_scores');
|
|
expect(h.records[0]).not.toHaveProperty('judge_reasoning');
|
|
expect(h.records[0]).not.toHaveProperty('reused_from');
|
|
expect(existsSync(join(root, 'cache'))).toBe(false);
|
|
});
|
|
|
|
test('an approval cannot turn low scores, malformed output, or a refusal-named ordinary error into acceptance', async () => {
|
|
const root = fixture(); approveFixture(root);
|
|
for (const result of [
|
|
{ ...passingScore, clarity: 1 },
|
|
new SyntaxError('Malformed provider JSON'),
|
|
Object.assign(new Error('Synthetic refusal text'), { name: 'JudgeRefusalError' }),
|
|
]) {
|
|
const h = actualCookieCallback(root, { judge: async () => { if (result instanceof Error) throw result; return result; } });
|
|
await expect(h.run()).rejects.toThrow();
|
|
expect(h.records).toHaveLength(1);
|
|
expect(h.records[0].passed).toBe(false);
|
|
expect(h.records[0]).not.toHaveProperty('manual_review');
|
|
expect(isManualReviewEntry(h.records[0])).toBe(false);
|
|
}
|
|
const scored = actualCookieCallback(root);
|
|
await scored.run();
|
|
expect(scored.records[0]).toMatchObject({ passed: true, judge_scores: COOKIE_WORKFLOW_JUDGE.thresholds });
|
|
expect(scored.records[0]).not.toHaveProperty('manual_review');
|
|
});
|
|
|
|
test.each(['prompt', 'model', 'budget', 'thresholds', 'malformed', 'missing', 'evidence', 'response-evidence'])(
|
|
'the actual callback rejects an unapproved refusal: %s', async mismatch => {
|
|
const root = fixture(); const approval = approveFixture(root);
|
|
const error = refusal();
|
|
if (mismatch === 'prompt') writeFileSync(join(root, 'BROWSER.md'), browser.replace('Exact reference.', 'Changed reference.'));
|
|
if (mismatch === 'model') approval.model = 'different-model';
|
|
if (mismatch === 'budget') approval.max_tokens++;
|
|
if (mismatch === 'thresholds') approval.thresholds.clarity++;
|
|
if (mismatch === 'evidence') error.refusal.request_id = null;
|
|
if (mismatch === 'response-evidence') error.refusal.response_id = null;
|
|
writeFileSync(join(root, COOKIE_MANUAL_REVIEW_FILE), mismatch === 'malformed' ? '{}' : JSON.stringify(approval));
|
|
if (mismatch === 'missing') rmSync(join(root, COOKIE_MANUAL_REVIEW_FILE));
|
|
const h = actualCookieCallback(root, { judge: async () => { throw error; } });
|
|
await expect(h.run()).rejects.toThrow();
|
|
expect(h.records).toHaveLength(1);
|
|
expect(h.records[0].passed).toBe(false);
|
|
expect(h.records[0]).not.toHaveProperty('manual_review');
|
|
expect(existsSync(join(root, 'cache'))).toBe(false);
|
|
});
|
|
|
|
test('a late refusal stays a timeout despite exact approval', async () => {
|
|
const root = fixture(); approveFixture(root);
|
|
let now = 0;
|
|
const h = actualCookieCallback(root, { budget: 20, clock: () => now,
|
|
judge: async () => { now = 20; throw refusal(); } });
|
|
await expect(h.run()).rejects.toThrow('deadline');
|
|
expect(h.records).toHaveLength(1);
|
|
expect(h.records[0]).toMatchObject({ passed: false, exit_reason: 'timeout' });
|
|
expect(h.records[0]).not.toHaveProperty('manual_review');
|
|
});
|
|
|
|
test('a retry refusal cannot erase an earlier scored failure with manual acceptance', async () => {
|
|
const root = fixture(); approveFixture(root);
|
|
let calls = 0;
|
|
const h = actualCookieCallback(root, { judge: async () => {
|
|
if (++calls === 1) return { ...passingScore, clarity: 1 };
|
|
throw refusal();
|
|
} });
|
|
await expect(h.run()).rejects.toThrow();
|
|
await expect(h.run()).rejects.toThrow('provider refused');
|
|
expect(h.records.map(record => [record.attempt, record.passed, record.exit_reason]))
|
|
.toEqual([[1, false, 'validation_failed'], [2, false, 'provider_refusal']]);
|
|
for (const record of h.records) expect(record).not.toHaveProperty('manual_review');
|
|
});
|
|
|
|
test('preserves exact source bytes, line ranges, and the immutable rubric', () => {
|
|
const input = buildCookieWorkflowJudgeInput(fixture());
|
|
expect(input.files).toEqual([
|
|
{ path: 'setup-browser-cookies/SKILL.md', kind: 'entrypoint', startLine: 2, endLine: 4, content: skill.slice(skill.indexOf('# Setup')) },
|
|
{ path: 'BROWSER.md', kind: 'section', startLine: 2, endLine: 4, content: browser.slice(browser.indexOf('#### Choosing'), browser.indexOf('### Tabs')) },
|
|
]);
|
|
for (const file of input.files) {
|
|
expect(input.text).toContain(`--- BEGIN FILE ${JSON.stringify(file.path)} (lines ${file.startLine}-${file.endLine}; ${file.kind}) ---\n${file.content}\n--- END FILE ${JSON.stringify(file.path)} ---`);
|
|
expect(input.text.split(file.content)).toHaveLength(2);
|
|
}
|
|
expect(input.prompt).toBe(buildWorkflowJudgePrompt(COOKIE_WORKFLOW_JUDGE, input));
|
|
expect(input.sha256).toBe(createHash('sha256').update(input.prompt).digest('hex'));
|
|
expect(input.prompt).not.toContain('excluded');
|
|
expect(COOKIE_WORKFLOW_JUDGE.thresholds).toEqual({ clarity: 4, completeness: 3, actionability: 4 });
|
|
});
|
|
|
|
test('preserves CRLF instead of rewriting excerpts', () => {
|
|
const input = buildCookieWorkflowJudgeInput(fixture(skill.replaceAll('\n', '\r\n'), browser.replaceAll('\n', '\r\n')));
|
|
expect(input.files[0].content).toBe(skill.slice(skill.indexOf('# Setup')).replaceAll('\n', '\r\n'));
|
|
expect(input.files[1].content).toBe(browser.slice(browser.indexOf('#### Choosing'), browser.indexOf('### Tabs')).replaceAll('\n', '\r\n'));
|
|
expect(input.files.map(file => [file.startLine, file.endLine])).toEqual([[2, 4], [2, 4]]);
|
|
});
|
|
|
|
test('fingerprints consumed bytes, not excluded source text', () => {
|
|
const original = buildCookieWorkflowJudgeInput(fixture());
|
|
expect(buildCookieWorkflowJudgeInput(fixture(skill.replace('Exact body.', 'Changed body.'))).sha256).not.toBe(original.sha256);
|
|
expect(buildCookieWorkflowJudgeInput(fixture(skill, browser.replace('Exact reference.', 'Changed reference.'))).sha256).not.toBe(original.sha256);
|
|
expect(buildCookieWorkflowJudgeInput(fixture(skill.replace('excluded preamble', 'different preamble'), browser.replace('excluded tab commands', 'different tab commands'))).sha256).toBe(original.sha256);
|
|
});
|
|
|
|
test('reads the actual generated workflow and only its referenced cookie section', () => {
|
|
const input = buildCookieWorkflowJudgeInput(ROOT);
|
|
const entry = readFileSync(join(ROOT, 'setup-browser-cookies/SKILL.md'), 'utf8');
|
|
const reference = readFileSync(join(ROOT, 'BROWSER.md'), 'utf8');
|
|
expect(input.files[0].content).toBe(entry.slice(entry.indexOf('# Setup Browser Cookies')));
|
|
expect(input.files[1].content).toBe(reference.slice(reference.indexOf('#### Choosing a source and checking sign-in'), reference.indexOf('### Tabs + frames')));
|
|
expect(input.files[0].content.length).toBeGreaterThan(500);
|
|
expect(input.files[1].content.length).toBeGreaterThan(500);
|
|
expect(input.prompt).toContain('GSTACK_COOKIE_AUTH_EXPECTED_IDENTITY');
|
|
expect(input.prompt).toContain('isolated world');
|
|
expect(input.prompt).not.toContain('### Tabs + frames');
|
|
});
|
|
|
|
test('fails closed when either source file is absent', () => {
|
|
expect(() => buildCookieWorkflowJudgeInput(fixture(null))).toThrow();
|
|
expect(() => buildCookieWorkflowJudgeInput(fixture(skill, null))).toThrow();
|
|
});
|
|
|
|
test('rejects missing, duplicate, or non-heading markers', () => {
|
|
for (const broken of [skill.replace('# Setup Browser Cookies', '# Missing'), skill + '# Setup Browser Cookies\nDuplicate\n', skill.replace('# Setup Browser Cookies', 'Quoted # Setup Browser Cookies')]) {
|
|
expect(() => buildCookieWorkflowJudgeInput(fixture(broken))).toThrow('expected exactly one marker');
|
|
}
|
|
for (const marker of ['#### Choosing a source and checking sign-in', '### Tabs + frames']) {
|
|
expect(() => buildCookieWorkflowJudgeInput(fixture(skill, browser.replace(marker, 'missing')))).toThrow('expected exactly one marker');
|
|
expect(() => buildCookieWorkflowJudgeInput(fixture(skill, browser + marker + '\n'))).toThrow('expected exactly one marker');
|
|
}
|
|
});
|
|
|
|
test('rejects empty, reversed, or unreferenced excerpts', () => {
|
|
expect(() => buildCookieWorkflowJudgeInput(fixture('# Setup Browser Cookies\n'))).toThrow('empty or reversed');
|
|
expect(() => buildCookieWorkflowJudgeInput(fixture(skill, '#### Choosing a source and checking sign-in\n\n### Tabs + frames\n'))).toThrow('empty or reversed');
|
|
expect(() => buildCookieWorkflowJudgeInput(fixture(skill, '### Tabs + frames\n#### Choosing a source and checking sign-in\nContent\n'))).toThrow('empty or reversed');
|
|
expect(() => buildCookieWorkflowJudgeInput(fixture(skill.replace('`BROWSER.md`', '`other.md`')))).toThrow('missing cookie reference link');
|
|
expect(() => buildCookieWorkflowJudgeInput(fixture(skill.replace('**Choosing a source and checking sign-in**', '**Other section**')))).toThrow('missing cookie reference link');
|
|
});
|
|
|
|
test('generated host workflows resolve shared references from the installation root and check Aside first', () => {
|
|
for (const file of ['setup-browser-cookies/SKILL.md', '.agents/skills/gstack-setup-browser-cookies/SKILL.md']) {
|
|
const source = readFileSync(join(ROOT, file), 'utf8');
|
|
const body = source.slice(source.indexOf('# Setup Browser Cookies'));
|
|
expect(body).toContain('Use this checkout as the gstack root if it contains `BROWSER.md` and `browse/SKILL.md`');
|
|
expect(body).toContain('the installed root containing `bin/gstack-skill-start`, never a generated host stub');
|
|
expect(body).toContain("Read that root's `browse/SKILL.md`");
|
|
expect(body).toContain('On `READY`, stop importing');
|
|
expect(body.indexOf('**BROWSER SETUP**')).toBeLessThan(body.indexOf('## SETUP'));
|
|
expect(body).toContain("Read that same root's `BROWSER.md`, **Choosing a source and checking sign-in**");
|
|
expect(body).not.toContain('../BROWSER.md');
|
|
}
|
|
expect(readFileSync(join(ROOT, 'browse/SKILL.md'), 'utf8')).toContain('## BROWSER SETUP (Aside');
|
|
expect(readFileSync(join(ROOT, 'BROWSER.md'), 'utf8')).toContain('#### Choosing a source and checking sign-in');
|
|
});
|
|
|
|
test('each owned dependency selects this judge in the fast PR profile', () => {
|
|
for (const file of ['setup-browser-cookies/SKILL.md.tmpl', 'setup-browser-cookies/SKILL.md', 'BROWSER.md', 'test/helpers/cookie-workflow-judge-input.ts', 'test/cookie-workflow-judge-input.test.ts', 'test/helpers/cookie-workflow-manual-review.ts', 'test/cookie-workflow-manual-review.test.ts', 'test/helpers/manual-judge-review-fixture.ts', '.github/cookie-workflow-manual-review.json']) {
|
|
const selectedJudges = selectTests([file], LLM_JUDGE_TOUCHFILES).selected;
|
|
expect(selectedJudges).toEqual([NAME]);
|
|
const selectedE2E = selectTests([file], E2E_TOUCHFILES).selected;
|
|
const profile = selectPrProfile({ changedFiles: [file], selectedJudges, selectedE2E });
|
|
expect(profile.judges).toEqual([NAME]);
|
|
expect(profile.deferredPromptFiles).toEqual([]);
|
|
expect(profile.missingCoverage).toEqual([]);
|
|
expect(profile.needsFullValidation).toBe(false);
|
|
}
|
|
expect(selectTests(['README.md'], LLM_JUDGE_TOUCHFILES).selected).not.toContain(NAME);
|
|
});
|
|
|
|
test('the managed callback sends and records exact custom input without borrowing a normal cache identity', async () => {
|
|
const root = fixture();
|
|
const input = buildCookieWorkflowJudgeInput(root);
|
|
let scores = passingScore;
|
|
const h = actualCookieCallback(root, { judge: async () => scores });
|
|
await h.run();
|
|
expect(h.requests).toHaveLength(1);
|
|
expect(h.requests[0].prompt).toBe(input.prompt);
|
|
expect(h.requests[0].model).toBeUndefined();
|
|
expect(h.requests[0].signal).toBeInstanceOf(AbortSignal);
|
|
expect(h.records[0]).toMatchObject({ name: NAME, prompt: input.prompt, model: 'fixture-model', execution: 'executed', passed: true });
|
|
expect(existsSync(join(root, 'cache'))).toBe(false);
|
|
const fresh = actualCookieCallback(root);
|
|
await fresh.run();
|
|
expect(fresh.requests).toHaveLength(1);
|
|
for (const dimension of ['clarity', 'completeness', 'actionability'] as const) {
|
|
scores = { ...COOKIE_WORKFLOW_JUDGE.thresholds, [dimension]: COOKIE_WORKFLOW_JUDGE.thresholds[dimension] - 1, reasoning: 'Synthetic failing fixture score' };
|
|
await expect(h.run()).rejects.toThrow();
|
|
expect(h.records.at(-1)).toMatchObject({ passed: false, exit_reason: 'validation_failed', prompt: input.prompt });
|
|
}
|
|
expect(existsSync(join(root, 'cache'))).toBe(false);
|
|
});
|
|
|
|
test('custom input timeout aborts once and a late response cannot overwrite its successful retry', async () => {
|
|
const root = fixture();
|
|
let completeLate!: (value: FixtureScore) => void;
|
|
let calls = 0, now = 0, timerId = 0;
|
|
const timers = new Map<number, () => void>();
|
|
const h = actualCookieCallback(root, {
|
|
budget: 20, clock: () => now,
|
|
setTimer: ((callback: () => void) => { timers.set(++timerId, callback); return timerId; }) as any,
|
|
clearTimer: ((id: number) => { timers.delete(id); }) as any,
|
|
judge: async () => ++calls === 1 ? new Promise(resolve => { completeLate = resolve; }) : passingScore,
|
|
});
|
|
const expired = h.run().catch((error: Error) => error);
|
|
now = 20;
|
|
for (const callback of [...timers.values()]) callback();
|
|
expect((await expired as Error).message).toContain('deadline');
|
|
expect(h.records).toHaveLength(1);
|
|
expect(h.records[0]).toMatchObject({ passed: false, exit_reason: 'timeout', prompt: buildCookieWorkflowJudgeInput(root).prompt });
|
|
expect(h.requests[0].signal.aborted).toBe(true);
|
|
await h.run();
|
|
expect(h.records.map(record => record.passed)).toEqual([false, true]);
|
|
expect(h.attempts.get(NAME)?.attempt).toBe(2);
|
|
completeLate(passingScore);
|
|
await new Promise(resolve => setImmediate(resolve));
|
|
expect(h.records).toHaveLength(2);
|
|
expect(existsSync(join(root, 'cache'))).toBe(false);
|
|
});
|
|
|
|
test('a superseding custom-input attempt cancels its predecessor without stale recording', async () => {
|
|
let completeLate!: (value: FixtureScore) => void;
|
|
let calls = 0;
|
|
const h = actualCookieCallback(fixture(), {
|
|
judge: async () => ++calls === 1 ? new Promise(resolve => { completeLate = resolve; }) : passingScore,
|
|
});
|
|
const old = h.run().catch((error: Error) => error);
|
|
await h.run();
|
|
expect((await old as Error).name).toBe('WorkflowJudgeSuperseded');
|
|
expect(h.requests[0].signal.aborted).toBe(true);
|
|
completeLate(passingScore);
|
|
await new Promise(resolve => setImmediate(resolve));
|
|
expect(h.records.map(record => [record.passed, record.exit_reason])).toEqual([[false, 'cancelled'], [true, undefined]]);
|
|
});
|
|
});
|