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:
garrytan committed 2026-10-04 18:02:05 +00:00
1 parent 9b64b900c0
commit 30d2904c66
6 files changed
+30 -9

No files matched your search

+3 -3
View File
@@ -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));
+3 -2
View File
@@ -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}`);
+7 -2
View File
@@ -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, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
.replace(/\[/g, '&#91;').replace(/\]/g, '&#93;'), max);
.replace(/\[/g, '&#91;').replace(/\]/g, '&#93;').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. */