mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-28 07:32:14 +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
102 lines
5.7 KiB
TypeScript
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'; }
|
|
}
|