mirror of
https://github.com/garrytan/gstack.git
synced 2026-10-31 22:14:25 +01:00
0.3: published lines cannot break a fixed fence
evals.yml wraps collector-outcomes failures in a fixed three-backtick fence for the PR comment, so those lines (and eval:pass-rates output meant for the tracking issue) go through sanitizeFixedFenceLine: cleaned like every published line, with backticks turned into quotes. Inline published text also neutralizes backticks.
This commit is contained in:
1 parent
9b64b900c0
commit
30d2904c66
6 files changed
+30
-9
No files matched your search
@@ -22,7 +22,7 @@ import * as path from 'node:path';
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { panelVerdict, type TrialOutcomeRecord } from '../../test/helpers/eval-store';
|
||||
import { HEADROOM_FAIL, HEADROOM_WARN } from '../../test/helpers/eval-budgets';
|
||||
import { sanitizePublishedText } from './published-text';
|
||||
import { sanitizeFixedFenceLine } from './published-text';
|
||||
import type { HistoryFetcher, WeeklyRun } from '../eval-flake-rank';
|
||||
import { CASE_SHARDED_FILES, trialShardKey } from './paid-cases';
|
||||
import { shardSlug } from '../test-paid-shards';
|
||||
@@ -204,7 +204,7 @@ export function formatRedLedger(ledger: RedLedger, opts: { top?: number } = {}):
|
||||
for (const red of c.reds) {
|
||||
const failed = red.trials.filter(t => t.outcome === 'failed');
|
||||
lines.push(` ✗ ${red.case} ${red.status} ${failed.map(t => `t${t.trial} ${t.failure_class ?? 'assertion'}/${t.failure_cause ?? 'unrecorded'}`
|
||||
+ `${t.error ? ` — ${sanitizePublishedText(t.error, 160)}` : ''}`).join('; ')}`);
|
||||
+ `${t.error ? ` — ${sanitizeFixedFenceLine(t.error, 160)}` : ''}`).join('; ')}`);
|
||||
}
|
||||
}
|
||||
const p = ledger.pooled;
|
||||
@@ -275,7 +275,7 @@ export function triageRun(opts: { repo: string; run: WeeklyRun; records: readonl
|
||||
const d = t.failure_detail;
|
||||
const detail = !d ? '' : 'judge' in d ? ` ${d.judge.map(j => `${j.dimension} ${j.mean} < ${j.threshold} (${j.samples} samples)${j.rationale ? ` "${j.rationale}"` : ''}`).join('; ')}`
|
||||
: ` Expected: ${d.expected} · Received: ${d.received}`;
|
||||
lines.push(sanitizePublishedText(` t${t.trial}: ${t.failure_class ?? 'assertion'} / cause ${t.failure_cause ?? 'unrecorded'}`
|
||||
lines.push(sanitizeFixedFenceLine(` t${t.trial}: ${t.failure_class ?? 'assertion'} / cause ${t.failure_cause ?? 'unrecorded'}`
|
||||
+ `${t.failure_cause_evidence ? `: ${t.failure_cause_evidence}` : ''}${t.error ? ` — ${t.error}` : ''}${detail}`, 2000));
|
||||
const plan = planFor(red.lane);
|
||||
const slice = plan.files.get(recordShardKey(t));
|
||||
|
||||
@@ -36,7 +36,7 @@ import {
|
||||
trialCostKnown, trialFailureFields, trialSessions, type TrialFailureDetail,
|
||||
} from '../../test/helpers/eval-store';
|
||||
import { readSessionLedger, type SessionLedgerRow } from '../../test/helpers/session-ledger';
|
||||
import { publishedFence, sanitizePublishedText } from './published-text';
|
||||
import { publishedFence, sanitizeFixedFenceLine } from './published-text';
|
||||
import { E2E_KINDS } from '../../test/helpers/touchfiles-data';
|
||||
import { manualReviewProblem } from '../../test/helpers/cookie-workflow-manual-review';
|
||||
import { preflightAnthropicApi } from '../../test/helpers/anthropic-preflight';
|
||||
@@ -772,7 +772,8 @@ export function runPaidReport(reportDir: string, options: { writeDurations?: boo
|
||||
marks: p.marks, split: p.split, quarantined: p.quarantined, failsLane: p.failsLane, redClass: p.redClass, reason: p.reason,
|
||||
trials: p.trials.map(t => ({ trial: t.trial, outcome: t.outcome, ...(t.failure_class ? { failure_class: t.failure_class } : {}),
|
||||
...(t.exit_reason ? { exit_reason: t.exit_reason } : {}), ...(t.error ? { error: t.error } : {}) })) })),
|
||||
failures: failureLines.map(line => sanitizePublishedText(line)) }, null, 2) + '\n');
|
||||
// evals.yml wraps these in a fixed fence for the PR comment.
|
||||
failures: failureLines.map(line => sanitizeFixedFenceLine(line)) }, null, 2) + '\n');
|
||||
if (verdict.problems.length) {
|
||||
console.error(`[test:paid] report: ${verdict.problems.length} problem(s):`);
|
||||
for (const problem of verdict.problems) console.error(` ✗ ${problem}`);
|
||||
|
||||
@@ -21,10 +21,15 @@ function clean(text: string): string {
|
||||
return redacted.replace(/@(?=[A-Za-z0-9_-])/g, '@\u200b');
|
||||
}
|
||||
|
||||
/** One inline line: cleaned, HTML and Markdown link brackets escaped, capped. */
|
||||
/** One inline line: cleaned, HTML, Markdown link brackets and backticks neutralized, capped. */
|
||||
export function sanitizePublishedText(text: string, max = PUBLISHED_TEXT_MAX): string {
|
||||
return cap(clean(text).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
|
||||
.replace(/\[/g, '[').replace(/\]/g, ']'), max);
|
||||
.replace(/\[/g, '[').replace(/\]/g, ']').replace(/`/g, "'"), max);
|
||||
}
|
||||
|
||||
/** One line for a consumer that wraps it in a fixed three-backtick fence (the PR comment): backticks become quotes. */
|
||||
export function sanitizeFixedFenceLine(text: string, max = PUBLISHED_TEXT_MAX): string {
|
||||
return cap(clean(text).replace(/`/g, "'"), max);
|
||||
}
|
||||
|
||||
/** Cleaned, capped lines inside a fence longer than the longest backtick run they contain. */
|
||||
|
||||
Reference in new issue
Block a user