mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-09 22:48:57 +02:00
fix: pre-landing review fixes for the v2 port wave
Review army (checklist + 5 specialists) + coverage/plan audits on the assembled branch. Genuine correctness/security/hygiene fixes: - test-paid-shards: strictTestExitCode now receives expectedFiles on the real bun path, so a shard that runs fewer files than planned (harness crash, nothing loaded) with exit 0 is no longer recorded 'passed' — the invisible-non-execution class the runner exists to kill. Pinned by the new test/strict-output.test.ts (also covers the chunk-boundary classifier). - test-paid-shards: EVALS_TIER env is validated (gate|periodic) like the --tier flag, so a typo can't self-skip every test and exit 0 green. - package.json: test:periodic:sharded sets EVALS_ALL=1, restoring the full-tier semantics the pre-shard script had (CI already set it; local eval:bg:periodic silently under-measured without it). - brain-sync.test: run() pins HOME to the temp home so gstack-artifacts-init stops writing/clobbering the operator's real ~/.gstack-artifacts-remote.txt every free-suite run; afterEach now also scrubs the current filename. - egress-receipt: cap each receipt field at 512B so a serialized line always fits the 4KB tail-read window — a longer line would make the next append hash a truncated prior line and verifyLedger report a permanent false TAMPER. warnLedgerSize short-circuits before statSync once fired (append hot path). - gstack-egress: import.meta.dir (Windows-safe) instead of new URL().pathname so grants doesn't silently report defaults on Windows; strip control chars from ledger-derived fields on render so a crafted receipt can't spoof the auditor's view. - extension/background.js + CLAUDE.md: renumber the identity-pin migration refs v1.62 -> v1.63 (main claimed 1.62.0.0; this wave queue-advances). - egress-receipt-wiring: pin lib/context-bill.ts unconditionally (both land together now); drop the dead RunShardsOptions.tier field. All fix-affected test files green; gate failures triaged as external-env (codex/gemini CLI drift) or pre-existing (hermetic-canary fails identically on base). Deferred polish tracked in the PR body + decision store. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
ea7ba921ce
commit
ccb91c3afb
@@ -167,7 +167,6 @@ export interface ShardCommand {
|
||||
}
|
||||
|
||||
export interface RunShardsOptions {
|
||||
tier?: PaidTier;
|
||||
timeoutMs?: number;
|
||||
jobs?: number;
|
||||
rootDir?: string;
|
||||
@@ -284,9 +283,17 @@ export async function runPaidShard(
|
||||
const summary = classifier.end();
|
||||
if (!streamLive && buffered.length > 0) process.stdout.write(Buffer.concat(buffered));
|
||||
|
||||
// Pass expectedFiles so a shard whose bun child ran fewer files than planned
|
||||
// (or zero, all self-skipped) with exit 0 is NOT recorded 'passed' — the
|
||||
// invisible-non-execution class this runner exists to kill. bun prints
|
||||
// "Ran N tests across M files" with M = selected files even when every test
|
||||
// self-skips, so terminalFileCounts must include files.length. Only enforced
|
||||
// on the real bun path: an injected commandFor (tests) isn't bun and emits no
|
||||
// terminal summary, so there's no file count to check against.
|
||||
const expectedFiles = options.commandFor ? undefined : files.length;
|
||||
const status: ShardStatus = timedOut
|
||||
? 'timed-out'
|
||||
: strictTestExitCode(exitCode ?? 1, summary) === 0 ? 'passed' : 'failed';
|
||||
: strictTestExitCode(exitCode ?? 1, summary, expectedFiles) === 0 ? 'passed' : 'failed';
|
||||
const elapsedMs = Date.now() - startedAt;
|
||||
log(`${label} ${status.toUpperCase()} in ${Math.round(elapsedMs / 1000)}s (exit ${exitCode ?? 'signal'})`);
|
||||
|
||||
@@ -387,9 +394,21 @@ function parsePositiveInt(value: string | undefined, flag: string): number {
|
||||
return parsed;
|
||||
}
|
||||
|
||||
function validatedTier(value: string | undefined, source: string): PaidTier {
|
||||
if (value === undefined || value === '') return DEFAULT_TIER;
|
||||
// A typo'd EVALS_TIER (e.g. 'e2e', the tier string eval-store uses) would
|
||||
// otherwise cast through unchecked, match nothing in the runtime E2E_TIERS
|
||||
// filter, self-skip every test, and exit 0 with all shards 'passed' — the
|
||||
// exact 0%-execution-looks-like-a-pass class this runner exists to kill.
|
||||
if (value !== 'gate' && value !== 'periodic') {
|
||||
throw new Error(`${source} must be gate or periodic. Received: ${value}`);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
export function parseCliOptions(argv: string[], env: NodeJS.ProcessEnv = process.env): CliOptions {
|
||||
const options: CliOptions = {
|
||||
tier: (env.EVALS_TIER as PaidTier) || DEFAULT_TIER,
|
||||
tier: validatedTier(env.EVALS_TIER, 'EVALS_TIER'),
|
||||
listOnly: false,
|
||||
timeoutMs: env.EVALS_SHARD_TIMEOUT_MS
|
||||
? parsePositiveInt(env.EVALS_SHARD_TIMEOUT_MS, 'EVALS_SHARD_TIMEOUT_MS')
|
||||
@@ -439,7 +458,8 @@ async function main(): Promise<number> {
|
||||
}
|
||||
|
||||
const summary = await runPaidShards(shards, {
|
||||
tier: options.tier,
|
||||
// Tier reaches the children only via EVALS_TIER below; the runtime
|
||||
// E2E_TIERS filter inside each child is the real selection mechanism.
|
||||
timeoutMs: options.timeoutMs,
|
||||
jobs: options.jobs,
|
||||
env: { ...process.env, EVALS: '1', EVALS_TIER: options.tier },
|
||||
|
||||
Reference in New Issue
Block a user