From b33c2d365de4a98bef1d15fb2c223c4b913f34d4 Mon Sep 17 00:00:00 2001 From: Christopher Date: Tue, 7 Jul 2026 10:39:43 +1000 Subject: [PATCH] fix(results): hard-remove legacy grading.json compat reads in manifest.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Found while auditing av-kfik.46.4/46.5. manifest.ts still tolerantly read a pre-major-version grading.json shape (assertion_results, verdict-as-pass, graders/evaluators as aliases for component_results) — while validate.ts already treats those same field names as hard errors. One code path rejected the legacy shape, another silently accepted it. No production users exist yet for this still-unreleased major version, so there's no on-disk bundle to preserve compatibility for. Removed rather than tested: - readGradingAssertionResults()/mapGradingAssertions() (assertion_results fallback) — dead, call site now derives directly from the component itself. - graders/evaluators branches in readNestedGradingScores() and hydrateManifestRecord()'s type augmentation — the `evaluators` fallback was explicitly commented as a v4.13-era TODO to remove once old run directories are gone. - verdict/score-threshold fallback for deriving `pass` — `pass` is always present per the current grading.json contract. Verified: 252 tests across apps/cli/test/commands/results pass; Dashboard's EvalDetail.test.ts already asserts these exact legacy fields are ignored (unaffected, no changes needed there — it was already written correctly). Live check: real eval run -> `results summary`/`results show` against the resulting bundle render correctly through the simplified code path. Co-Authored-By: Claude Sonnet 5 --- apps/cli/src/commands/results/manifest.ts | 54 +++-------------------- 1 file changed, 5 insertions(+), 49 deletions(-) diff --git a/apps/cli/src/commands/results/manifest.ts b/apps/cli/src/commands/results/manifest.ts index 999f13772..35a8b0aa9 100644 --- a/apps/cli/src/commands/results/manifest.ts +++ b/apps/cli/src/commands/results/manifest.ts @@ -130,30 +130,6 @@ export interface ManifestHydrationOptions { type HydratedScore = NonNullable[number]; -function mapGradingAssertions( - value: unknown, -): NonNullable | undefined { - if (!Array.isArray(value)) { - return undefined; - } - return value.map((assertion) => { - const record = assertion as Record; - return { - text: String(record.text ?? ''), - passed: Boolean(record.passed), - evidence: typeof record.evidence === 'string' ? record.evidence : undefined, - }; - }); -} - -function readGradingAssertionResults( - record: Record, -): NonNullable | undefined { - return mapGradingAssertions( - Array.isArray(record.assertion_results) ? record.assertion_results : record.assertions, - ); -} - function readNestedGradingScores(record: Record): unknown { if (Array.isArray(record.component_results)) { return record.component_results; @@ -161,12 +137,6 @@ function readNestedGradingScores(record: Record): unknown { if (Array.isArray(record.scores)) { return record.scores; } - if (Array.isArray(record.graders)) { - return record.graders; - } - if (Array.isArray(record.evaluators)) { - return record.evaluators; - } return undefined; } @@ -209,11 +179,7 @@ function collectComponentAssertions(value: unknown): NonNullable): HydratedScore { - const pass = - typeof evaluator.pass === 'boolean' - ? evaluator.pass - : evaluator.verdict === 'pass' || - (typeof evaluator.score === 'number' && evaluator.score >= 0.8); + const pass = evaluator.pass === true; const verdict = pass ? ('pass' as const) : ('fail' as const); const details = evaluator.details && typeof evaluator.details === 'object' && !Array.isArray(evaluator.details) @@ -236,7 +202,7 @@ function mapGradingEvaluator(evaluator: Record): HydratedScore if (nestedAssertions.length > 0) { return nestedAssertions; } - return readGradingAssertionResults(evaluator) ?? [mapComponentAssertion(evaluator)]; + return [mapComponentAssertion(evaluator)]; })(), scores: mapGradingEvaluators(readNestedGradingScores(evaluator)), weight: typeof evaluator.weight === 'number' ? evaluator.weight : undefined, @@ -401,15 +367,8 @@ function hydrateManifestRecord( const gradingAssertions = grading ? collectComponentAssertions((grading as unknown as Record).component_results) : undefined; - const gradingRecord = grading as - | (GradingArtifact & { - graders?: readonly Record[]; - evaluators?: readonly Record[]; - }) - | undefined; - const gradingScores = mapGradingEvaluators( - gradingRecord?.component_results ?? gradingRecord?.graders ?? gradingRecord?.evaluators, - ); + const gradingRecord = grading as GradingArtifact | undefined; + const gradingScores = mapGradingEvaluators(gradingRecord?.component_results); return { timestamp: record.timestamp, @@ -426,10 +385,7 @@ function hydrateManifestRecord( passed: assertion.passed, evidence: assertion.evidence, })), - scores: - // `evaluators` was renamed to `graders` in v4.13 — read both for backwards compat with old artifacts. - // TODO: remove `evaluators` fallback once old run directories are no longer in use. - gradingScores ?? (record.scores as EvaluationResult['scores']), + scores: gradingScores ?? (record.scores as EvaluationResult['scores']), tokenUsage: metrics?.tokens ? { input: metrics.tokens.input,