mirror of
https://github.com/garrytan/gstack.git
synced 2026-10-02 17:40:02 +02:00
refactor(evals): share the import-closure walker and add the E2E shard reuse identity
sourceDependencyClosure moves from the workflow-judge adapter into scripts/eval-input-cache.ts unchanged, so judge keys stay byte-identical. scripts/e2e-shard-reuse.ts builds the consumed-input identity of one PR-lane E2E shard (test import closure, every registered case's touchfiles, globals, runner/workflow/setup actions, child env pins, CI image, Claude CLI) and fails closed on anything unknown. Marathon joins the always-fresh purposes.
This commit is contained in:
1 parent
f29d2ee015
commit
acb7bc02b1
3 files changed
+233
-38
No files matched your search
@@ -1,13 +1,12 @@
|
||||
/** Audited cache adapter for runWorkflowJudge only. Native/PTY evals stay fresh. */
|
||||
import * as fs from 'node:fs';
|
||||
import * as path from 'node:path';
|
||||
import { isBuiltin } from 'node:module';
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { DEFAULT_JUDGE_MAX_TOKENS, resolveEvalModel } from '../../lib/eval-model';
|
||||
import { JUDGE_MS } from './eval-budgets';
|
||||
import type { JudgeScore } from './llm-judge';
|
||||
import { readWorkflowJudgeInput, buildWorkflowJudgePrompt, WORKFLOW_JUDGE_RESPONSE_SCHEMA, WORKFLOW_JUDGE_REASONING_WORD_LIMIT } from './workflow-judge-input';
|
||||
import { buildEvalInputIdentity, lookupEvalInputCache, storeEvalInputCache,
|
||||
import { buildEvalInputIdentity, lookupEvalInputCache, sourceDependencyClosure, storeEvalInputCache,
|
||||
type EvalCacheValue, type EvalInputIdentity, type EvalPassingProof } from '../../scripts/eval-input-cache';
|
||||
|
||||
type Thresholds = { clarity: number; completeness: number; actionability: number };
|
||||
@@ -25,44 +24,13 @@ export interface WorkflowJudgeReuse {
|
||||
key: string; source: EvalPassingProof['source'];
|
||||
}
|
||||
|
||||
/** Follow literal module imports, including installed SDK bytes, without executing them. */
|
||||
/** The judge's audited closure: its runner, rubric and documents, installed SDK bytes included. */
|
||||
export function workflowJudgeDependencies(root: string, documents: string[]): string[] {
|
||||
const seen = new Set<string>();
|
||||
const scan = new Bun.Transpiler({ loader: 'tsx' });
|
||||
const visit = (file: string) => {
|
||||
file = path.resolve(file);
|
||||
const relative = path.relative(root, file).split(path.sep).join('/');
|
||||
if (relative.startsWith('../') || path.isAbsolute(relative)) throw new Error('Dependency outside checkout');
|
||||
// Root version labels collector output only; its remaining semantic fields
|
||||
// are hashed separately. Installed package manifests remain byte-exact.
|
||||
if (relative === 'package.json') return;
|
||||
if (seen.has(relative)) return;
|
||||
seen.add(relative);
|
||||
const source = fs.readFileSync(file, 'utf8');
|
||||
if (!/\.[cm]?[jt]sx?$/.test(file)) return;
|
||||
// Entrypoint scripts carry hashbangs, which scanImports does not accept.
|
||||
// Strip only for parsing; buildEvalInputIdentity still hashes the full file.
|
||||
for (const entry of scan.scanImports(source.replace(/^#![^\n]*(?:\n|$)/, '\n'))) {
|
||||
if (isBuiltin(entry.path) || entry.path.startsWith('bun:')) continue;
|
||||
const resolved = Bun.resolveSync(entry.path, path.dirname(file));
|
||||
visit(resolved);
|
||||
// Package export maps/defaults affect resolution independently of code.
|
||||
let directory = path.dirname(resolved);
|
||||
while (directory !== root && directory.startsWith(root + path.sep)) {
|
||||
const manifest = path.join(directory, 'package.json');
|
||||
if (fs.existsSync(manifest)) { visit(manifest); break; }
|
||||
directory = path.dirname(directory);
|
||||
}
|
||||
}
|
||||
};
|
||||
for (const file of ['test/skill-llm-eval.test.ts', 'test/helpers/workflow-judge-cache.ts',
|
||||
return sourceDependencyClosure(root, ['test/skill-llm-eval.test.ts', 'test/helpers/workflow-judge-cache.ts',
|
||||
'test/helpers/llm-judge.ts', 'lib/eval-model.ts', 'test/helpers/eval-budgets.ts',
|
||||
'scripts/test-paid-shards.ts', 'scripts/test-strict-output.ts', 'scripts/eval-select.ts',
|
||||
'scripts/test-pr-profile.ts', '.github/workflows/evals.yml',
|
||||
'package.json', 'bun.lock', '.github/docker/Dockerfile.ci', ...documents]) visit(path.join(root, file));
|
||||
for (const file of ['bunfig.toml', 'tsconfig.json', 'jsconfig.json'])
|
||||
if (fs.existsSync(path.join(root, file))) visit(path.join(root, file));
|
||||
return [...seen].sort();
|
||||
'package.json', 'bun.lock', '.github/docker/Dockerfile.ci', ...documents]);
|
||||
}
|
||||
|
||||
export function validWorkflowJudgeScore(value: EvalCacheValue, thresholds: Thresholds, structuredResponse = false): value is JudgeScore & EvalCacheValue {
|
||||
|
||||
Reference in new issue
Block a user