mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-15 17:35:29 +02:00
fix: claude CLI version resolves in the runner parent, never on a test thread
Eng-review finding: getClaudeCliVersion's fallback is a SYNCHRONOUS spawnSync on the same thread that polls concurrent PTY/session tests — the judgePtyState blocking class this overhaul kills elsewhere. The paid runner parent now resolves it once (cached) and stamps GSTACK_CLAUDE_CLI_VERSION into every shard's env; eval-store short-circuits on the env var, and the fallback spawn's budget tightens 10s -> 3s (bounded one-time stall, records 'unknown' on a slow CLI). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
cfcc9ecc6f
commit
31bb69f9b4
@@ -64,7 +64,7 @@ import {
|
|||||||
} from './test-strict-output';
|
} from './test-strict-output';
|
||||||
import { PAID_TEST_GLOBS, isPaidTestFile } from '../test/helpers/paid-test-set';
|
import { PAID_TEST_GLOBS, isPaidTestFile } from '../test/helpers/paid-test-set';
|
||||||
import { PERIODIC_CI_EXCLUDE } from '../test/helpers/periodic-exclude-data';
|
import { PERIODIC_CI_EXCLUDE } from '../test/helpers/periodic-exclude-data';
|
||||||
import { getProjectEvalDir } from '../test/helpers/eval-store';
|
import { getProjectEvalDir, getClaudeCliVersion } from '../test/helpers/eval-store';
|
||||||
import { preflightAnthropicApi } from '../test/helpers/anthropic-preflight';
|
import { preflightAnthropicApi } from '../test/helpers/anthropic-preflight';
|
||||||
import {
|
import {
|
||||||
detectBaseBranch,
|
detectBaseBranch,
|
||||||
@@ -493,6 +493,12 @@ export async function runPaidShard(
|
|||||||
if (options.evalDirBase) {
|
if (options.evalDirBase) {
|
||||||
env.GSTACK_EVAL_DIR = path.join(options.evalDirBase, 'shards', shardSlug(files));
|
env.GSTACK_EVAL_DIR = path.join(options.evalDirBase, 'shards', shardSlug(files));
|
||||||
}
|
}
|
||||||
|
// Resolve `claude --version` ONCE in the parent (cached across shards) and
|
||||||
|
// hand it to every child: eval-store's fallback is a synchronous spawn on
|
||||||
|
// the same thread that polls PTY sessions, so children must never pay it.
|
||||||
|
if (!env.GSTACK_CLAUDE_CLI_VERSION) {
|
||||||
|
env.GSTACK_CLAUDE_CLI_VERSION = getClaudeCliVersion();
|
||||||
|
}
|
||||||
|
|
||||||
const startedAt = Date.now();
|
const startedAt = Date.now();
|
||||||
log(`${label} START ${files.join(' ')} (timeout ${Math.round(timeoutMs / 1000)}s)`);
|
log(`${label} START ${files.join(' ')} (timeout ${Math.round(timeoutMs / 1000)}s)`);
|
||||||
|
|||||||
@@ -786,11 +786,23 @@ function getVersion(): string {
|
|||||||
// claude-CLI TUI drift only after long flake hunts — stamping the version
|
// claude-CLI TUI drift only after long flake hunts — stamping the version
|
||||||
// into every run record makes that correlation a grep instead of an
|
// into every run record makes that correlation a grep instead of an
|
||||||
// archaeology dig.
|
// archaeology dig.
|
||||||
|
//
|
||||||
|
// GSTACK_CLAUDE_CLI_VERSION short-circuits the spawn entirely: the paid
|
||||||
|
// runner's parent resolves the version once and passes it to every shard,
|
||||||
|
// so test processes never block on it. The fallback spawn is SYNCHRONOUS on
|
||||||
|
// the same thread that polls PTY sessions — the judgePtyState blocking
|
||||||
|
// class — so its budget is a tight 3s, not a generous one: a slow/hung CLI
|
||||||
|
// costs one bounded stall per process and records 'unknown'.
|
||||||
let claudeCliVersionCache: string | null = null;
|
let claudeCliVersionCache: string | null = null;
|
||||||
export function getClaudeCliVersion(): string {
|
export function getClaudeCliVersion(): string {
|
||||||
if (claudeCliVersionCache !== null) return claudeCliVersionCache;
|
if (claudeCliVersionCache !== null) return claudeCliVersionCache;
|
||||||
|
const fromEnv = process.env.GSTACK_CLAUDE_CLI_VERSION;
|
||||||
|
if (fromEnv) {
|
||||||
|
claudeCliVersionCache = fromEnv;
|
||||||
|
return claudeCliVersionCache;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const result = spawnSync('claude', ['--version'], { stdio: 'pipe', timeout: 10_000 });
|
const result = spawnSync('claude', ['--version'], { stdio: 'pipe', timeout: 3_000 });
|
||||||
claudeCliVersionCache = result.stdout?.toString().split('\n')[0].trim() || 'unknown';
|
claudeCliVersionCache = result.stdout?.toString().split('\n')[0].trim() || 'unknown';
|
||||||
} catch {
|
} catch {
|
||||||
claudeCliVersionCache = 'unknown';
|
claudeCliVersionCache = 'unknown';
|
||||||
|
|||||||
Reference in New Issue
Block a user