mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-15 01:15:29 +02:00
feat: paid-lane flake telemetry — record-level attempts, flaky_retries, report surfacing
bun --retry leaves a retried pass INVISIBLE in its output: a fail-then-pass prints the error detail but no (fail) result line and recaps as a clean pass (probed live on 1.3.10). So attempts are recorded where they cannot lie: EvalCollector.addTest stamps a 1-based attempt on same-name re-records (a retried test runs its body again and re-records), finalized runs carry flaky_retries, printSummary warns loudly, and the fail-closed slices report lists every passed-only-on-retry test — recorded and ranked, never blocking and never silent. Cross-model confirmed (codex reached the same don't-parse -the-stream conclusion independently). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
cf2990b9fa
commit
2dabc02447
@@ -63,6 +63,12 @@ export interface EvalTestEntry {
|
||||
passed: boolean;
|
||||
duration_ms: number;
|
||||
cost_usd: number;
|
||||
/** 1-based record attempt for this name in this run. bun's --retry leaves
|
||||
* retried passes INVISIBLE in its text output (a fail→pass prints no
|
||||
* (fail) line and recaps as a clean pass — probed on 1.3.10), so the ONLY
|
||||
* reliable attempt signal is this in-process record: a retried test runs
|
||||
* its body again and re-records under the same name. Set by addTest. */
|
||||
attempt?: number;
|
||||
|
||||
// E2E
|
||||
transcript?: any[];
|
||||
@@ -132,6 +138,11 @@ export interface EvalResult {
|
||||
tests: EvalTestEntry[];
|
||||
/** Shard slug when the run was collected under <evalDir>/shards/<slug>/. */
|
||||
shard?: string;
|
||||
/** Tests recorded more than once this run — the flake ledger for the paid
|
||||
* lane. A test passing on attempt 2 every week used to read permanently
|
||||
* green (the retry's entry was indistinguishable and bun's output hides
|
||||
* retries entirely). Present only when non-empty. */
|
||||
flaky_retries?: Array<{ name: string; attempts: number }>;
|
||||
_partial?: boolean; // true for incremental saves, absent in final
|
||||
}
|
||||
|
||||
@@ -825,10 +836,23 @@ export class EvalCollector {
|
||||
}
|
||||
|
||||
addTest(entry: EvalTestEntry): void {
|
||||
this.tests.push(entry);
|
||||
// Same-name re-record = the test body ran again = bun retried it (test
|
||||
// names are unique by convention). Stamp the 1-based attempt so a
|
||||
// pass-on-attempt-2 stays visible forever — the stream hides it.
|
||||
const prior = this.tests.filter((t) => t.name === entry.name).length;
|
||||
this.tests.push({ ...entry, attempt: prior + 1 });
|
||||
this.savePartial();
|
||||
}
|
||||
|
||||
/** Names recorded more than once this run, with their attempt counts. */
|
||||
private flakyRetries(): Array<{ name: string; attempts: number }> {
|
||||
const counts = new Map<string, number>();
|
||||
for (const t of this.tests) counts.set(t.name, (counts.get(t.name) ?? 0) + 1);
|
||||
return [...counts.entries()]
|
||||
.filter(([, n]) => n > 1)
|
||||
.map(([name, attempts]) => ({ name, attempts }));
|
||||
}
|
||||
|
||||
/** Write incremental results after each test. Atomic write, non-fatal. */
|
||||
savePartial(): void {
|
||||
try {
|
||||
@@ -893,6 +917,7 @@ export class EvalCollector {
|
||||
wall_clock_ms: Date.now() - this.createdAt,
|
||||
tests: this.tests,
|
||||
...(this.shard ? { shard: this.shard } : {}),
|
||||
...(this.flakyRetries().length > 0 ? { flaky_retries: this.flakyRetries() } : {}),
|
||||
};
|
||||
|
||||
// Write eval file
|
||||
@@ -955,6 +980,12 @@ export class EvalCollector {
|
||||
const totalCost = `$${result.total_cost_usd.toFixed(2)}`;
|
||||
const totalDur = `${Math.round(result.total_duration_ms / 1000)}s`;
|
||||
lines.push(` Total: ${result.passed}/${result.total_tests} passed${' '.repeat(20)}${totalCost.padStart(6)} ${totalDur}`);
|
||||
if (result.flaky_retries && result.flaky_retries.length > 0) {
|
||||
// Loud, never fatal: a flaky pass must not block anyone, but it must
|
||||
// never be silent either — that invisibility is how flakes calcified.
|
||||
lines.push(` ⚠ FLAKY: ${result.flaky_retries.length} test(s) recorded multiple attempts this run: `
|
||||
+ result.flaky_retries.map((f) => `${f.name} (x${f.attempts})`).join(', '));
|
||||
}
|
||||
lines.push(`Saved: ${filepath}`);
|
||||
|
||||
process.stderr.write(lines.join('\n') + '\n');
|
||||
|
||||
Reference in New Issue
Block a user