Repository navigation
chore(eval): eval through the Claude Code CLI, scored by two judges - #105
Merged
Merged
Conversation
0xMMA
added a commit
that referenced
this pull request
Oct 11, 2026
PR #105 rebased onto main as one commit (the original eleven are on backup/eval-via-claude-code-pre-rebase). Pipeline and judge run through the installed Claude Code CLI by default; the judge records temperature null plus a note; claudeCodeVersion is recorded; .env keys load only when an API provider is in the run. Claude Code CLI calls run in an empty KeyLint-owned directory with auto-memory off, and inherited thinking, output-cap and alias-remapping variables are stripped. Resolved against main: - Resolved model IDs: main's Response.Model, answeringModel and llm.ModelRecorder replace the PR's ResolvedModels; summary.json keeps main's shape (model = resolved ID, modelRequested). The judge records its own resolvedModel through a second recorder. UsesAPIKey and SupportsTemperature move to internal/llm/traits.go. - Thinking: main's Request.NoThinking replaces the PR's DisableThinking. The CLI keeps ignoring it, as on main, so EVAL_THINKING / --thinking are gone; a Fix run records what the service does (fixThinkingLabel). - Judge: main's noEffort and the PR's nil temperature and deadline. - Effort, model families, typed errors and the rest of main unchanged. Co-Authored-By: Claude <noreply@anthropic.com>
0xMMA
force-pushed
the
chore/eval-via-claude-code
branch
from
October 11, 2026 17:44
dfbea5d to
78ad7bb
Compare
0xMMA
added a commit
that referenced
this pull request
Oct 11, 2026
PR #105 rebased onto main as one commit (the original eleven are on backup/eval-via-claude-code-pre-rebase). Pipeline and judge run through the installed Claude Code CLI by default; the judge records temperature null plus a note; claudeCodeVersion is recorded; .env keys load only when an API provider is in the run. Claude Code CLI calls run in an empty KeyLint-owned directory with auto-memory off, and inherited thinking, output-cap and alias-remapping variables are stripped. Resolved against main: - Resolved model IDs: main's Response.Model, answeringModel and llm.ModelRecorder replace the PR's ResolvedModels; summary.json keeps main's shape (model = resolved ID, modelRequested). The judge records its own resolvedModel through a second recorder. UsesAPIKey and SupportsTemperature move to internal/llm/traits.go. - Thinking: main's Request.NoThinking replaces the PR's DisableThinking. The CLI keeps ignoring it, as on main, so EVAL_THINKING / --thinking are gone; a Fix run records what the service does (fixThinkingLabel). - Judge: main's noEffort and the PR's nil temperature and deadline. - Effort, model families, typed errors and the rest of main unchanged. Co-Authored-By: Claude <noreply@anthropic.com>
0xMMA
force-pushed
the
chore/eval-via-claude-code
branch
from
October 11, 2026 18:21
ee2132f to
fecbfaf
Compare
0xMMA
added a commit
that referenced
this pull request
Oct 11, 2026
--setting-sources "" does not stop Claude Code's auto-memory: with KeyLint's working directory inherited, the memory file of the matching ~/.claude/projects/<slug> was injected into every Fix and Pyramidize call (measured 2026-10-04, while building the CLI eval in #105). - Every spawn, the status probes included, runs in an empty KeyLint-owned directory under the user cache dir. Without a cache dir it uses a private os.MkdirTemp directory for the process (random name, 0700) instead of a shared fixed path; if that fails too the call fails with a clear error rather than inheriting the cwd. - CLAUDE_CODE_DISABLE_AUTO_MEMORY=1 is set; an inherited value cannot turn it back on. - Inherited alias-remapping (ANTHROPIC_DEFAULT_*_MODEL, ANTHROPIC_SMALL_FAST_MODEL), thinking-budget and output-cap variables are stripped like the credential ones. - Tests point the cache dir at a temp dir on every OS. Co-authored-by: Michael <0xMMA@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com>
PR #105 rebased onto main as one commit (the original eleven are on backup/eval-via-claude-code-pre-rebase). Pipeline and judge run through the installed Claude Code CLI by default; the judge records temperature null plus a note; claudeCodeVersion is recorded; .env keys load only when an API provider is in the run. Claude Code CLI calls run in an empty KeyLint-owned directory with auto-memory off, and inherited thinking, output-cap and alias-remapping variables are stripped. Resolved against main: - Resolved model IDs: main's Response.Model, answeringModel and llm.ModelRecorder replace the PR's ResolvedModels; summary.json keeps main's shape (model = resolved ID, modelRequested). The judge records its own resolvedModel through a second recorder. UsesAPIKey and SupportsTemperature move to internal/llm/traits.go. - Thinking: main's Request.NoThinking replaces the PR's DisableThinking. The CLI keeps ignoring it, as on main, so EVAL_THINKING / --thinking are gone; a Fix run records what the service does (fixThinkingLabel). - Judge: main's noEffort and the PR's nil temperature and deadline. - Effort, model families, typed errors and the rest of main unchanged. Co-Authored-By: Claude <noreply@anthropic.com>
Haiku 5.5 and Sonnet 5.5 judge each sample in both suites, both through the Claude Code CLI by default (decision 2026-10-11, E3 phase 1). Full model IDs: neither has a dated snapshot, and each judge records the ID that answered. - summary.json: judges[] (with resolvedModel), avgJudges, judgeCounts; results.jsonl: judges / judgeErrors keyed by judge model. - eval-aggregate.sh: one interval per judge (run and sample level), per-judge coverage; nothing averages across judges. Runs from before the set (judge / avgJudge) read as a set of one. - configKey: the judge provider, resolved model and temperature fields hold one "+"-joined entry per judge, ordered by ID. A single-judge key is unchanged, so every recorded baseline reads "not comparable" against a two-judge run instead of crashing. - --compare: interval rule per metric (deterministic, each judge); any metric clearly worse is a regression, an improvement needs one clearly better and none worse. - EVAL_JUDGES=a,b,c chooses the set (bridge run with the old Sonnet 4.5 judge); EVAL_JUDGE_MODEL is a set of one; both, a hole or a duplicate are refused. eval.sh shows the set; go test timeout 10800 s. Co-Authored-By: Claude <noreply@anthropic.com>
Instrument note only, no new numbers: the single-judge baselines read "not comparable" against a two-judge run until new ones are recorded. Co-Authored-By: Claude <noreply@anthropic.com>
Review follow-ups on the two-judge instrument: - Per-judge columns are keyed by the ID that answered, as the configKey is. Keyed by the requested name, a baseline judged as "haiku" and a run judged as "claude-haiku-5-5" shared a key but no column, and a drop from 0.95 to 0.40 read "inconclusive", exit 0. A judge with no interval on one side is now "not comparable", never "overlap". - SupportsTemperature is model-aware: on the API, Claude models from the 4.7 generation on take no temperature, so the 5.5 judges record null with a note instead of sending 0 (a 400). The Sonnet 4.5 bridge judge keeps temperature 0. - claudeCodeVersion leaves the configKey (the CLI updates itself); the baseline lists every version seen and --compare reports a change. The output names the metrics that flagged a verdict (regressedMetrics, improvedMetrics), and only for a comparison actually made. - eval.sh: its env -u list is tested against blockedCLIEnv; --dry-run asks the CLI nothing, --preflight adds the sign-in check; stale comments fixed. - Docs: API-era baselines are not comparable with this instrument on any route; the 2026-10-04 CLI baselines are marked as possibly affected by the auto-memory leak (#127) and superseded, and the readings built on them are flagged unconfirmed. Co-Authored-By: Claude <noreply@anthropic.com>
0xMMA
force-pushed
the
chore/eval-via-claude-code
branch
from
October 11, 2026 18:57
fecbfaf to
3862a51
Compare
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.
What it does
Both eval suites (Pyramidize and Fix) run through the installed Claude Code CLI by default, and every sample is scored by two judges,
claude-haiku-5-5andclaude-sonnet-5-5, each reported on its own. This is E3 phase 1, step "Instrument" (decisions of 2026-10-11).CLI eval (the original #105, rebased):
claude-code, so no API key is read.--provider claudeandEVAL_JUDGE_PROVIDER=claudeswitch to the API..envkeys load only when an API provider is in the run.ModelRecorder(modelholds the ID, plusmodelRequested). Each judge records its ownresolvedModel.claudeCodeVersionis read at the start and end of a run and reported next to a verdict. It is not keyed (decision 2026-10-11), because the CLI updates itself.Two judges:
summary.jsonhasjudges[],avgJudgesandjudgeCounts.eval-aggregate.shandbaseline.jsonhold one interval per judge, at run level and per sample, plus per-judge coverage. Nothing averages across judges.haikuandclaude-haiku-5-5) land in one column.configKey: the judge provider, resolved model and temperature fields hold one+-joined entry per judge, ordered by ID.test-data/eval-baselines/through this.--compare: applies the interval rule to each metric (deterministic, then each judge).regressedMetrics/improvedMetricsname what flagged the verdict, so a regression carried by one judge alone reads as that. The fields are filled only for a comparison that was actually made.EVAL_JUDGES=a,b,cchooses the set. Bridge run:EVAL_JUDGES=claude-haiku-5-5,claude-sonnet-5-5,claude-sonnet-4-5-20250929.EVAL_JUDGE_MODELis a set of one. Both variables at once, an empty entry or a duplicate are refused.llm.SupportsTemperature(provider, model)):nullplus a note).nullwith a note instead of sending 0 and getting a 400. The Sonnet 4.5 bridge judge stays at 0.modelUsagereported exactly those IDs.eval.sh:go test -timeoutis 10800 s.--dry-runasks the CLI nothing;--preflightadds the sign-in check.env -ulist is tested againstblockedCLIEnv.Review follow-ups (Opus review)
Conflicts resolved against main
Response.Model,answeringModelandllm.ModelRecorder(feat: model family defaults, effort per feature, AI Providers redesign #108) are kept; the PR'sResolvedModelsis dropped.Request.NoThinkingreplaces the PR'sDisableThinking. The CLI keeps ignoring it, as on main, soEVAL_THINKING/--thinkingare gone. A Fix run recordsfixThinkingLabel: CLIon, fast-tier APIoff, otherwisen/a.noEffortplus the PR's nil temperature and 180 s deadline.d471baf. That commit's message still describes the isolation, which now lives in fix(llm): isolate Claude Code CLI calls from the user's sessions #127; rewording it was blocked here. It does not matter after a squash merge.Verification
go vet -tags gtk3 ./...andgo vet -tags eval,gtk3 ./internal/...: clean.go test -tags gtk3 ./internal/...: pass. Every new behaviour test was seen failing first: the resolved-ID columns, missing interval, metric naming, CLI version not keyed, model-aware temperature, env-list sync and dry-run.go build -tags gtk3 -o bin/KeyLint .: ok.cd frontend && npm test: 37 files, 496 tests pass.wire gen/ bindings: not needed.Smoke run (made before the review fixes; nothing re-run since)
One sample per suite, filtered with
go test -run. 6 model calls, plus 5 non-model CLI invocations (--version×4,auth status×1).en-grammar-basicshaiku→claude-haiku-5-5email-meeting-requestsonnet→claude-sonnet-5-5These are single samples, not measurements. The Fix smoke sample came from the holdout split; future smoke runs should use a tune sample, e.g.
EVAL_SPLIT=tune go test -tags eval ./internal/features/enhance/ -run 'TestEvalFix/^interpunktion$' -v.Not done