F: cost_known: a trial whose harness captured no billing says so; totals never hide it

cost_usd stays a finite number. Trial and eval records gain an optional
cost_known boolean (absent means known, so old records still parse; the
validator accepts only a boolean). Session-ledger rows record whether the
session's terminal result carried total_cost_usd; PTY and Codex sessions
never do, and SDK captures without a terminal result do not. A trial is
cost-known only when it has eval records and every record and session
captured billing, so JUnit-only PTY cases (125 of 298 trials in run
37198445662 recorded 0) now read cost unknown. The census headline prints the
known sum plus the unknown count, never a bare total over unknown costs, and
no total when every cost is unknown; the history import keeps the flag.
PTY transcripts are not priced: test/helpers/pricing.ts estimates exclude
cache writes, so they cannot give the billed cost.
This commit is contained in:
garrytan committed 2026-10-04 17:57:55 +00:00
1 parent 6d6c7726ec
commit 9b64b900c0
11 files changed
+136 -14

No files matched your search

+1 -1
View File
@@ -265,7 +265,7 @@ export function backfillEvalFiles(files: string[], run?: { run_id: string; sha?:
tier: caseTier(id, registry), kind: registry.kinds[id]!, trial: 1, panel: { n: 1, k: 1 }, attempt: 1,
outcome, ...(outcome === 'failed' ? { failure_class: failureClassOf(entry) } : {}),
exit_reason: entry.exit_reason, error: sanitizeTrialError(entry.error),
duration_ms: Math.max(0, entry.duration_ms || 0), cost_usd: Math.max(0, entry.cost_usd || 0),
duration_ms: Math.max(0, entry.duration_ms || 0), cost_usd: Math.max(0, entry.cost_usd || 0), ...(entry.cost_known === false ? { cost_known: false } : {}),
model: entry.model, cli_version: result.claude_cli_version, policy_version: 0, quarantined: false,
execution: entry.execution === 'reused' ? 'reused' : 'executed', source: 'backfill',
run_id: run?.run_id ?? `local:${file}`, sha: run?.sha ?? result.git_sha, recorded_at: run?.timestamp ?? result.timestamp,
+13 -6
View File
@@ -33,7 +33,7 @@ import {
getProjectEvalDir, getClaudeCliVersion, isFinalizedEvalResultFile, evalEntryOutcome, failureClassOf, panelVerdict,
sanitizeTrialError, formatTrialOutcomes, CONTRACT_VIOLATIONS_FILE, TRIAL_ENV, TRIAL_OUTCOME_SCHEMA, TRIAL_OUTCOMES_FILE,
type EvalCaseKind, type PanelShape, type PanelVerdict, type TrialFailureClass, type TrialOutcome, type TrialOutcomeRecord,
trialFailureFields, trialSessions, type TrialFailureDetail,
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';
@@ -319,7 +319,7 @@ export function junitCensus(manifest: PaidRunManifest, artifacts: ReportArtifact
census.history.push({ ...base, case: id, file: shardFile(key), kind, trial: 1, panel: { n: 1, k: 1 }, outcome: caseOutcome,
...(failureClass ? { failure_class: failureClass } : {}), ...(failedRecord?.exit_reason ? { exit_reason: String(failedRecord.exit_reason) } : {}),
...(error && failed ? { error } : {}), ...diagnosis, ...trialSessions(sessions), duration_ms: cases.reduce((sum, tc) => sum + tc.timeMs, 0),
cost_usd: Math.round(mine.reduce((sum: number, r: any) => sum + (Number(r.cost_usd) || 0), 0) * 100) / 100,
cost_usd: Math.round(mine.reduce((sum: number, r: any) => sum + (Number(r.cost_usd) || 0), 0) * 100) / 100, ...trialCostKnown(mine, sessions),
...(typeof mine[0]?.model === 'string' ? { model: mine[0].model } : {}), ...(cliVersion ? { cli_version: cliVersion } : {}),
quarantined: false, execution: outcome.reused ? 'reused' : 'executed', source: 'junit' } as TrialOutcomeRecord);
}
@@ -482,7 +482,13 @@ export interface ReportHeadline {
redispatchEligible: boolean;
}
export function formatHeadline(h: ReportHeadline): string[] {
/** Known cost, never a bare total when any trial's cost is unknown, and no total when none is known. */
export function formatCost(knownUsd: number, unknownTrials: number, trials: number): string {
if (trials > 0 && unknownTrials === trials) return `cost unknown (no billing captured for any of ${trials} trial(s))`;
return `cost $${knownUsd.toFixed(2)}${unknownTrials ? ` known + ${unknownTrials} of ${trials} trial(s) cost unknown` : ''}`;
}
export function formatHeadline(h: ReportHeadline, cost: { unknownTrials: number; trials: number } = { unknownTrials: 0, trials: 0 }): string[] {
const c = h.counts;
const minutes = h.wallMs === null ? 'unknown' : `${Math.floor(h.wallMs / 60_000)}m${String(Math.round((h.wallMs % 60_000) / 1000)).padStart(2, '0')}s`;
return [
@@ -490,7 +496,7 @@ export function formatHeadline(h: ReportHeadline): string[] {
` rule ${c.rule.passed}/${c.rule.total} · behavior ${c.behavior.passed}/${c.behavior.total}${c.behavior.split ? ` (${c.behavior.split} split)` : ''}`
+ ` · judge ${c.judge.passed}/${c.judge.total} · quarantined ${c.quarantined.total} (${c.quarantined.failingLane} failing the lane)`,
` SKIPPED ${c.skipped} · INFRA ${c.infra} · INCOMPLETE ${c.incomplete} · unattributed ${c.unattributed} · ACTION REQUIRED ${h.actionRequired}`,
` wall ${minutes} · cost $${h.costUsd.toFixed(2)}${h.redispatchEligible ? ' · every red is machine-classified INFRA/INCOMPLETE: eligible for ONE re-dispatch as a new run (EVAL_POLICY.infraRedispatch); report both runs' : ''}`,
` wall ${minutes} · ${formatCost(h.costUsd, cost.unknownTrials, cost.trials)}${h.redispatchEligible ? ' · every red is machine-classified INFRA/INCOMPLETE: eligible for ONE re-dispatch as a new run (EVAL_POLICY.infraRedispatch); report both runs' : ''}`,
];
}
@@ -675,7 +681,7 @@ export function runPaidReport(reportDir: string, options: { writeDurations?: boo
...(t.outcome === 'failed' && t.failure_cause ? { failure_cause: t.failure_cause } : {}),
...(t.outcome === 'failed' && t.failure_cause_evidence ? { failure_cause_evidence: t.failure_cause_evidence } : {}),
...(t.outcome === 'failed' && t.failure_detail ? { failure_detail: t.failure_detail } : {}), ...(t.sessions ? { sessions: t.sessions } : {}),
duration_ms: t.duration_ms, cost_usd: t.cost_usd, ...(t.model ? { model: t.model } : {}),
duration_ms: t.duration_ms, cost_usd: t.cost_usd, ...(t.cost_known === false ? { cost_known: false } : {}), ...(t.model ? { model: t.model } : {}),
...(outcome.reused ? { input_identity: outcome.reused.inputKey } : {}),
quarantined: t.quarantined, execution: outcome.reused ? 'reused' : 'executed', source: 'shard' } as TrialOutcomeRecord);
}
@@ -742,7 +748,8 @@ export function runPaidReport(reportDir: string, options: { writeDurations?: boo
costUsd: Math.round(costUsd * 100) / 100,
redispatchEligible: red && infraOnly(verdict.problems),
};
const headlineLines = formatHeadline(headline);
const executed = history.filter(r => r.attempt === primary && r.execution === 'executed' && r.outcome !== 'skipped');
const headlineLines = formatHeadline(headline, { unknownTrials: executed.filter(r => r.cost_known === false).length, trials: executed.length });
for (const line of headlineLines) console.log(line);
if (failureLines.length) {
console.log('[test:paid] failures and split verdicts:');
+3 -1
View File
@@ -83,7 +83,7 @@ import {
getProjectEvalDir, getClaudeCliVersion, isFinalizedEvalResultFile, evalEntryOutcome, failureClassOf, panelVerdict,
sanitizeTrialError, formatTrialOutcomes, CONTRACT_VIOLATIONS_FILE, TRIAL_ENV, TRIAL_OUTCOME_SCHEMA, TRIAL_OUTCOMES_FILE,
type EvalCaseKind, type PanelShape, type PanelVerdict, type TrialFailureClass, type TrialOutcome, type TrialOutcomeRecord,
trialFailureFields, trialSessions, type TrialFailureCause, type TrialFailureDetail, type TrialSessionSummary,
trialCostKnown, trialFailureFields, trialSessions, type TrialFailureCause, type TrialFailureDetail, type TrialSessionSummary,
} from '../test/helpers/eval-store';
import { readSessionLedger, type SessionLedgerRow } from '../test/helpers/session-ledger';
import { E2E_KINDS } from '../test/helpers/touchfiles-data';
@@ -726,6 +726,7 @@ export interface ShardTrialRecord {
failure_cause_evidence?: string;
failure_detail?: TrialFailureDetail;
sessions?: TrialSessionSummary[];
cost_known?: false;
}
type TrialEvidence = { records: any[]; contract: string | null; sessions?: SessionLedgerRow[] };
@@ -759,6 +760,7 @@ export function classifyTrialShard(
cost_usd: Math.round(evidence.records.reduce((sum, record) => sum + (Number(record.cost_usd) || 0), 0) * 100) / 100,
duration_ms: outcome.elapsedMs,
...(typeof failedRecord?.model === 'string' ? { model: failedRecord.model } : {}), ...trialSessions(evidence.sessions ?? []),
...trialCostKnown(evidence.records, evidence.sessions),
};
const failed = (failureClass: TrialFailureClass, error?: string): ShardTrialRecord => {
const cls = evidence.contract !== null ? 'contract' : failureClass;