Files
gstack/test/helpers/cookie-workflow-manual-review.ts
T
Garry Tan a84b0b5b6d v1.90.0.0 feat: make browser cookie imports explicit and safe (#2964)
* 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
2026-09-25 12:06:45 -04:00

102 lines
5.7 KiB
TypeScript

import { createHash } from 'node:crypto';
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
import { buildCookieWorkflowJudgeInput, COOKIE_WORKFLOW_JUDGE } from './cookie-workflow-judge-input';
import type { JudgeRefusalEvidence } from './llm-judge';
import { DEFAULT_JUDGE_MAX_TOKENS, resolveEvalModel } from '../../lib/eval-model';
export const COOKIE_MANUAL_REVIEW_FILE = '.github/cookie-workflow-manual-review.json';
const CASE = 'setup-browser-cookies/SKILL.md workflow';
type Thresholds = { clarity: number; completeness: number; actionability: number };
export interface CookieManualApproval {
schema_version: 1;
test_name: typeof CASE;
prompt_sha256: string;
prompt_bytes: number;
model: string;
max_tokens: number;
thresholds: Thresholds;
approved_by: string;
approved_at: string;
approval_url: string;
reason: string;
}
export interface ManualJudgeReview {
approval: CookieManualApproval;
refusal: JudgeRefusalEvidence;
}
const object = (value: unknown): value is Record<string, any> => value !== null && typeof value === 'object' && !Array.isArray(value);
const nonempty = (value: unknown): value is string => typeof value === 'string' && value.trim().length > 0;
const dimensions = ['clarity', 'completeness', 'actionability'] as const;
const sameThresholds = (a: Thresholds, b: Thresholds) => dimensions.every(key => a[key] === b[key]);
function validApproval(value: unknown): value is CookieManualApproval {
return object(value) && value.schema_version === 1 && value.test_name === CASE
&& typeof value.prompt_sha256 === 'string' && /^[a-f0-9]{64}$/.test(value.prompt_sha256)
&& Number.isSafeInteger(value.prompt_bytes) && value.prompt_bytes > 0
&& nonempty(value.model) && Number.isSafeInteger(value.max_tokens) && value.max_tokens > 0
&& object(value.thresholds) && dimensions.every(key => Number.isInteger(value.thresholds[key]) && value.thresholds[key] >= 1 && value.thresholds[key] <= 5)
&& nonempty(value.approved_by) && typeof value.approved_at === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(value.approved_at)
&& typeof value.approval_url === 'string' && /^https:\/\/github\.com\/garrytan\/gstack\/pull\/2964#issuecomment-\d+$/.test(value.approval_url)
&& nonempty(value.reason);
}
function validRefusal(value: unknown, model: string): value is JudgeRefusalEvidence {
return object(value) && value.stop_reason === 'refusal' && value.model === model
&& nonempty(value.response_id) && nonempty(value.request_id)
&& Number.isSafeInteger(value.input_tokens) && value.input_tokens >= 0
&& value.output_tokens === 0 && value.text_blocks === 0;
}
export function isManualReviewEntry(entry: unknown): boolean {
if (!object(entry) || entry.name !== CASE || entry.suite !== 'Cookie setup workflow quality'
|| entry.tier !== 'llm-judge' || entry.passed !== false
|| entry.attempt !== 1
|| entry.execution !== 'executed' || entry.exit_reason !== 'provider_refusal'
|| entry.judge_scores !== undefined || entry.judge_reasoning !== undefined || entry.reused_from !== undefined
|| typeof entry.prompt !== 'string' || !object(entry.manual_review)) return false;
const { approval, refusal } = entry.manual_review;
return validApproval(approval) && entry.model === approval.model && validRefusal(refusal, approval.model)
&& Buffer.byteLength(entry.prompt) === approval.prompt_bytes
&& createHash('sha256').update(entry.prompt).digest('hex') === approval.prompt_sha256;
}
export function getCookieWorkflowManualReview(root: string, request: {
testName: string; prompt: string; model: string; maxTokens: number; thresholds: Thresholds; attempt: number;
}, refusal: JudgeRefusalEvidence): ManualJudgeReview | null {
if (request.testName !== CASE || request.attempt !== 1 || !validRefusal(refusal, request.model)) return null;
let approval: unknown;
try { approval = JSON.parse(readFileSync(join(root, COOKIE_MANUAL_REVIEW_FILE), 'utf8')); }
catch (error) {
if ((error as NodeJS.ErrnoException).code === 'ENOENT') return null;
throw error;
}
if (!validApproval(approval)) throw new Error('Invalid cookie workflow manual-review approval');
const current = buildCookieWorkflowJudgeInput(root);
if (request.prompt !== current.prompt || current.sha256 !== approval.prompt_sha256
|| Buffer.byteLength(request.prompt) !== approval.prompt_bytes || request.model !== approval.model
|| request.maxTokens !== approval.max_tokens || request.maxTokens !== DEFAULT_JUDGE_MAX_TOKENS
|| !sameThresholds(request.thresholds, approval.thresholds)
|| !sameThresholds(request.thresholds, COOKIE_WORKFLOW_JUDGE.thresholds)) return null;
return { approval, refusal };
}
export function manualReviewProblem(entry: unknown, root: string): string | null {
if (!object(entry) || !Object.hasOwn(entry, 'manual_review')) return null;
if (!isManualReviewEntry(entry)) return 'Malformed manual-review claim';
if (entry.model !== resolveEvalModel('judge')) return 'Manual review model is not the current judge model';
try {
const claimed = entry.manual_review as ManualJudgeReview;
const verified = getCookieWorkflowManualReview(root, { testName: entry.name, prompt: entry.prompt,
model: entry.model, maxTokens: claimed.approval.max_tokens, thresholds: claimed.approval.thresholds,
attempt: entry.attempt }, claimed.refusal);
if (!verified || Object.entries(verified.approval).some(([key, value]) => key === 'thresholds'
? !sameThresholds(value as Thresholds, claimed.approval.thresholds)
: value !== claimed.approval[key as keyof CookieManualApproval])) return 'Manual review does not match current source and approval';
return null;
} catch { return 'Manual review approval or current input is unavailable or invalid'; }
}