feat: improve eval setup for skill optimization + fix two scoring bugs#36
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Improves the
agent-ttyeval setup for skill optimization, and fixes two scoring bugs that were making absolute pass rates misleading (especially for Codex on the execution lane).Tooling improvements
--condition(evals/run.ts):--condition none --condition preloadedin one invocation, canonically ordered and deduped, with fail-fast validation. Replaces priorset-onceCLI behavior.evals/lib/reporting.ts,evals/lib/schemas.ts,evals/run.ts): promoted "Condition comparison" section (per-condition totals + key deltas:realizedSkillLift,oracleSkillLift,routingGap,staleSkillHarm,regressionRate,unlockRate,routingEfficiency) to the top ofreport.md. Added structuredconditionComparisonSummarytoreport.json. ResolvedoutputBaseDiris now visible in both human and JSON outputs.evals/README.md,evals/execution/runner.ts,evals/execution/cases/shared.ts): documented the 10 execution cases by readiness tier (battle-tested/non-renderer unproven/renderer-optional/renderer-required), and emitenvironment-blockederrorClass when transcripts indicate Playwright/Chromium/ghostty-web failures instead of conflating them with skill regressions.Scoring bug fixes (found while running experiments)
Running the full eval sweep across Claude (opus 4.6, high) and Codex (gpt-5.4, high) with trials surfaced a Codex "self-load collapse" on execution (0% pass rate). Diagnostic revealed this was not a routing/skill problem but two scoring bugs:
evals/lib/antiPatterns.ts). Inself-loadruns,agent-tty skills get agent-ttyechoed the skill doc into the transcript. The skill's own "avoid these" warnings (scrot,tmux,xdotool,gnome-screenshot) matched the anti-pattern regexes, producing up to 62 false-positive findings per run and tanking the anti-pattern-avoidance subscore.buildScannableTranscript()now scans only tool-call inputs (what the model intended to execute), not outputs. Prompt-lane behavior unchanged.evals/lib/scoring.ts).inferSelectedSkill()returned'dogfood-tui'on any literal mention, even sentences like "dogfood-tui would be too specialized because the user is not asking for QA." Now uses the existingisInNegationContext()helper to only count non-rejected mentions.Both fixes land with unit tests.
Paired baseline validation (post-fix vs pre-fix reports)
Using the new
--compare-baselineand--trialstooling from main (02cc7dd), with bootstrap-CI paired verdicts:Top per-case improvements (Codex execution, self-load):
hello-prompt,scrollback-demo,unicode-grid,resize-demo,crash-recoveryall went from 0% → 100% pass rate from the scoring fix alone.Validation
npm run typecheck— passesnpm run lint— passesnpm run test— 78 test files, 830 tests passingnpm run format:check— passes (scoped to tracked files; generatedevals/reports/is now gitignored)test/unit/evals/run.test.ts), reporting comparison summary (test/unit/evals/reporting.test.ts), anti-pattern scan regression guard (test/unit/evals/antiPatterns.test.ts), skill-selection negation cases (test/unit/evals/scoring.test.ts).Dogfooding proof
Cross-provider sweep with the new parallelization tooling (
--trials,--concurrency 4):Post-fix validation runs used
--compare-baselineagainst those baselines and produced the paired verdicts in the table above.All 8 eval reports (
report.json+report.mdper run) plus cross-providerCOMPARISON.mdand the finalEXPERIMENTS.mdwriteup are generated locally and reviewable. Per existing repo convention, they live underevals/reports/{timestamp}/which is gitignored — they were not committed.Commits
c3d71edfeat: allow repeating eval conditions5dd675etest: cover repeated eval conditions545f896feat: surface renderer blockers in execution evals5027152docs: document execution readiness tiers93a1a45feat: surface eval comparison summary53807a7style: fix formatting in eval files86ff7dafix: ignore echoed tool output in anti-pattern scans00559c8fix: ignore rejected dogfood-tui mentions in scoring0688484chore: gitignore generated eval reports📋 Implementation Plan
Plan: Improve the eval setup for skill optimization
Objective
Make the eval loop faster to iterate on skill text, easier to compare
nonevspreloaded/self-load, and clearer about which lanes and cases produce trustworthy signals.Repo-backed evidence
evals/run.tsis the entrypoint. Its help text documents--condition <cond>as singular, andparseCliArgs()currently enforces--condition may only be set oncewhile--caseis already repeatable.SkillCondition[]:resolveRequestedConditions()returns an array andSKILL_CONDITIONSlives inevals/lib/matrix.ts, so the narrowest high-leverage change is CLI ergonomics rather than a matrix rewrite.evals/reports/{timestamp}or a custom--outputviaresolveOutputBaseDir()andEvalArtifactStore; the real gap is cross-workspace discoverability/trend tracking, not a literal/tmpwrite path.doctor-gatedrequires renderer support,color-gridcan use renderer optionally, and the other 8 cases do not depend on Playwright/Chromium.evals/run.ts.Constraints and guiding choices
assertString,invariant, strict schemas, and canonical ordering fromSKILL_CONDITIONS.Workstream 1 — Make condition comparison a first-class workflow (P0)
Files:
evals/run.ts,evals/README.md, newtest/unit/evals/run.test.ts(andtest/unit/evals/matrix.test.tsonly if matrix expectations need adjustment)--conditioncan be repeated, matching--case.CliOptions.condition?: stringwithconditions: string[]and updateresolveRequestedConditions()to:--condition all --condition none,SKILL_CONDITIONSorder before execution/reporting.--condition none --condition preloaded.allexclusivity, invalid values, empty values, and dry-run matrix filtering.Acceptance:
nonevspreloaded(and/orself-load) without shell loops.--dry-run,--json, and human output all reflect the selected condition set.Workstream 2 — Surface the comparison signal we actually use (P1)
Files:
evals/run.ts,evals/lib/reporting.ts, andevals/lib/schemas.tsonly if metadata/report shape changesevals/lib/matrix.tsinreport.md/report.jsonfor the selected condition set:realizedSkillLift,routingGap,staleSkillHarm) when the required conditions are present.Acceptance:
Deferred choice: long-term history storage
Current code already persists runs under
evals/reports/{timestamp}or a user-supplied--output. The feedback about/tmpdoes not match the current implementation; the real issue is that workspace-local reports are easy to lose. Start by making--output/resolved paths obvious and by saving important runs to a stable location. Only add an opt-in history file or external store if the team still needs trend aggregation after the faster comparison loop is in place.Workstream 3 — Make Lane B coverage status explicit and expand it in the right order (P2)
Files:
evals/README.md,evals/execution/runner.tsif reporting needs better environment diagnostics, andevals/execution/cases/*or shared case metadata if the readiness labels live with the caseshello-prompt,crash-recovery,run-command,resize-demo,alt-screen-demo,scrollback-demo,unicode-grid,export-proof,color-grid,doctor-gated.environment-blockedoutcome over a silent skip.doctor --jsonis green.Acceptance:
Workstream 4 — Recalibrate secondary lanes without blocking skill work (P3)
Files:
evals/prompt/runner.ts,evals/prompt/cases/*,evals/dogfood/runner.ts,evals/dogfood/scorers/index.ts,evals/README.mdAcceptance:
Validation and quality gates
mise run test -- test/unit/evals/run.test.tsmise run test -- test/unit/evals/matrix.test.tsmise run testmise run typecheckmise run lintmise run format-checkif docs/report templates changed materiallyDogfooding and reviewer proof
mise install && mise run bootstrap(or the documented npm fallback),AGENT_HOME="$(mktemp -d)".npx tsx evals/run.ts --provider <provider> --lane execution --case hello-prompt --case run-command --case crash-recovery --condition none --condition preloaded --output <stable-dir>,dogfood/(or update an existing eval proof bundle) containing:report.jsonandreport.md,doctor --jsonoutput used to explain renderer availability.agent-tty --home "$AGENT_HOME" doctor --jsonagent-tty --home "$AGENT_HOME" create --json -- /bin/bashagent-tty --home "$AGENT_HOME" run "$SESSION_ID" '<eval command>'agent-tty --home "$AGENT_HOME" wait "$SESSION_ID" --text '<expected text>'agent-tty --home "$AGENT_HOME" screenshot "$SESSION_ID"agent-tty --home "$AGENT_HOME" record export "$SESSION_ID" --format webmdoctor --json,Non-goals for the first pass
Generated with
mux• Model:anthropic:claude-opus-4-7• Thinking:max