mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-08 09:48:20 +01:00
# Conflicts: # .github/workflows/evals-periodic.yml # CHANGELOG.md # CONTRIBUTING.md # TODOS.md # docs/TESTING_INTERNALS.md # scripts/eval-flake-rank.ts # scripts/lib/paid-report.ts # scripts/paid-test-durations.json # scripts/test-paid-shards.ts # test/eng-finding-retry-budget.test.ts # test/fixtures/module-size-ratchet.json # test/helpers/carve-guards.ts # test/helpers/touchfiles-data.ts # test/paid-retry-supervision.test.ts # test/qa-probe-gates.test.ts # test/review-workflow-clarity.test.ts # test/ship-control-flow.test.ts
133 lines
5.5 KiB
TypeScript
133 lines
5.5 KiB
TypeScript
/**
|
||
* Paid-lane shared types and constants: the leaf module every paid-lane file imports. Moved from scripts/test-paid-shards.ts
|
||
* so scripts/lib/paid-*.ts never import the CLI module back (no import cycle).
|
||
*/
|
||
import * as path from 'node:path';
|
||
import { normalizeRelativePath, type LanePolicy } from './shard-engine';
|
||
import type { EvalCaseKind, PanelShape, TrialFailureCause, TrialFailureClass, TrialFailureDetail, TrialOutcome, TrialSessionSummary } from '../../test/helpers/eval-store';
|
||
|
||
export const ROOT = path.resolve(import.meta.dir, '..', '..');
|
||
|
||
export type PaidTier = 'gate' | 'periodic' | 'marathon';
|
||
export const PAID_TIERS: readonly PaidTier[] = ['gate', 'periodic', 'marathon'];
|
||
export type PaidProfile = 'pr' | 'full';
|
||
|
||
export interface PaidCaseSelection {
|
||
e2e: string[] | null;
|
||
judges: string[] | null;
|
||
}
|
||
|
||
export const DEFAULT_TIER: PaidTier = 'gate';
|
||
export const DEFAULT_SHARD_TIMEOUT_MS = 30 * 60_000;
|
||
export const DEFAULT_MAX_FILES_PER_SHARD = 1;
|
||
// 8 jobs × 2 within-shard ≈ 10-13 real in-flight sessions (39 of 75
|
||
// skill-e2e files hold exactly ONE test, so within-shard concurrency is
|
||
// dead weight for most shards) — under the documented-safe ~15 the legacy
|
||
// 40-way runner established. The old 4×4 yielded only ~4-6 in-flight and a
|
||
// 13-wave local gate worst case (~6.5h); 8×2 halves it. Watch the WS1
|
||
// flake telemetry for sustained 429 storms across 2 PR cycles — that is
|
||
// the rollback trigger. Prerequisite (landed): per-shard TMPDIR/
|
||
// CHROMIUM_PROFILE isolation in runPaidShard.
|
||
export const DEFAULT_JOBS = 8;
|
||
export const DEFAULT_WITHIN_SHARD_CONCURRENCY = 2;
|
||
|
||
/**
|
||
* Paid-lane classification policy. Seeds keep only positive walls (a zero
|
||
* is not a real paid-shard measurement). A shard that passed with zero
|
||
* executed tests is legitimate under selection (in-file diff/tier
|
||
* self-skips) and only warns; under EVALS_ALL it is hollow: 'passed-empty'.
|
||
*/
|
||
export const PAID_LANE_POLICY: LanePolicy = {
|
||
acceptsSeedDuration: (ms) => ms > 0,
|
||
zeroExecution: ({ promisedAll }) => (promisedAll ? 'passed-empty' : 'passed-with-warning'),
|
||
};
|
||
|
||
/** One overlay process preserves the original process-wide SDK semaphore. */
|
||
export const OVERLAY_MAX_ACTIVE_SHARDS = 1;
|
||
|
||
export function isOverlayTestFile(file: string): boolean {
|
||
return /^skill-e2e-overlay-harness-.+\.test\.ts$/.test(path.basename(normalizeRelativePath(file)));
|
||
}
|
||
|
||
export interface PaidShardBudget {
|
||
timeoutMs: number;
|
||
source: 'explicit' | 'registered' | 'default';
|
||
policyId: string | null;
|
||
}
|
||
|
||
export type ShardStatus =
|
||
| 'passed'
|
||
| 'failed'
|
||
| 'timed-out'
|
||
| 'never-started'
|
||
| 'skipped-by-diff'
|
||
// exit 0 with ZERO executed tests on a run that promised everything
|
||
// (EVALS_ALL): the hollow-file green the census backstop exists to catch.
|
||
// Under selective runs, 0-executed passed shards stay 'passed' (in-file
|
||
// diff/tier self-skips are legitimate there) and get a WARNING line only.
|
||
| 'passed-empty';
|
||
|
||
export interface ShardOutcome {
|
||
shard: number;
|
||
files: string[];
|
||
status: ShardStatus;
|
||
exitCode: number | null;
|
||
elapsedMs: number;
|
||
groupPid: number | null;
|
||
/** Tests bun reported executing ("Ran N tests ..."), null when unknown. */
|
||
executedTests: number | null;
|
||
/** Tests bun reported skipping (" N skip" count line), null when unknown.
|
||
* "Ran N tests" COUNTS skips, so executedTests alone cannot distinguish a
|
||
* shard that verified work from one whose every test self-skipped —
|
||
* codex/gemini files green-by-skip on every CI runner (no binary) and the
|
||
* weekly census read them as covered. */
|
||
skippedTests: number | null;
|
||
/** Effective supervised wall; absent only for unstarted or legacy outcomes. */
|
||
budget?: PaidShardBudget;
|
||
/** Present when a verified receipt replaced execution (PR lane only). */
|
||
reused?: { inputKey: string; runId: string; revision: string; completedAt: number };
|
||
/** The parent could not run the shard at all (a runner error, never a trial verdict). */
|
||
runnerError?: string;
|
||
/** PR lane: the reuse input identity of a freshly executed shard whose inputs stayed unchanged. */
|
||
inputKey?: string;
|
||
/** Isolated trial shards only: the trial record this shard produced. */
|
||
trial?: ShardTrialRecord;
|
||
/** The slice's in-process deadline (job ceiling minus the upload reserve) decided this outcome:
|
||
* `not_run` = never started (INFRA), `hung` = killed in flight (TIMEOUT). */
|
||
sliceDeadline?: 'not_run' | 'hung';
|
||
}
|
||
|
||
/**
|
||
* One isolated trial's record, derived from its shard status and the records
|
||
* in its own eval dir. `outcome` null means the harness produced no trial
|
||
* (never started, hollow, isolation broken, runner error): the panel is then
|
||
* INCOMPLETE and the slice exits non-zero. A failed, timed-out or crashed
|
||
* trial is a trial verdict; the slice still exits zero and the report decides.
|
||
*/
|
||
export interface ShardTrialRecord {
|
||
case: string;
|
||
trial: number;
|
||
kind: EvalCaseKind;
|
||
panel: PanelShape;
|
||
quarantined: boolean;
|
||
outcome: TrialOutcome | null;
|
||
harness?: string;
|
||
failure_class?: TrialFailureClass;
|
||
exit_reason?: string;
|
||
error?: string;
|
||
timeout_at_turn?: number;
|
||
cost_usd: number;
|
||
duration_ms: number;
|
||
model?: string;
|
||
failure_cause?: TrialFailureCause;
|
||
failure_cause_evidence?: string;
|
||
failure_detail?: TrialFailureDetail;
|
||
sessions?: TrialSessionSummary[];
|
||
cost_known?: false;
|
||
}
|
||
|
||
/** Upload reserve between a slice's in-process deadline and its CI job ceiling (ENG-2). */
|
||
export const SLICE_UPLOAD_RESERVE_MS = 5 * 60_000;
|
||
/** Env var the executor workflows set at their first step: the job's start in epoch seconds. */
|
||
export const SLICE_JOB_STARTED_AT_ENV = 'GSTACK_SLICE_JOB_STARTED_AT';
|