This is advanced content, for users who want to understand how eval scores, how metrics are computed, and how automatic multi-turn evaluation runs. For day-to-day evaluation, Quickstart is enough.
Metric System at a Glance
Key distinction: rubric dimension scores and pass@k/pass^k are informational metrics, for diagnosis and comparison; the real pass/fail is
checks_failed == []. Most failures come from task validators (target_artifacts existence + test_scripts), but profiles can also write workflow-contract failures, such as missing required Skills, into checks_failed.Where the Evidence Comes From
Comet eval evidence comes from stream-json emitted directly by the Claude CLI. It is not guessed from terminal text, and it is not reconstructed from another model’s summary. Single-turn evaluation runs the subject Agent directly inside the task directory in Docker:stdout / stderr, then parses JSONL events from stdout line by line. The parser extracts:
duration_ms,num_turns, tokens, and cost fromresultevents- Tool calls from
tool_useevents Bashcommands ascommands_runWrite/Editpaths asfiles_created/files_modifiedSkillcalls asskills_invoked- Matching
tool_resultoutput attached back to the original tool call
Skills invoked, commands_run, tool_calls, files_created, token/cost, and duration are organized from Claude CLI’s structured event stream.
Trajectory and Event Stream Are Different
The Comet Classic runtime also writes.comet/trajectory*.jsonl, recording state progression such as state_transitioned. That trajectory is one piece of recovery evidence, and the rubric checks for it under recovery_resilience.
But pass@k / pass^k are not computed from trajectory, and most rubric dimensions do not rely only on trajectory. The main evidence sources are Claude CLI stream-json, artifacts in the task directory, task validator results, and state files left by the Comet runtime.
rubric Scoring
The rubric breaks a Skill’s quality down into multiple dimensions, each dimension composed of several binary (pass/fail) check items. The dimension score = passed items / total items (0.0–1.0). Each dimension outputs a line[RUBRIC] <dimension>: <score> - <reason>.
Scoring methodology (aligned with industry practices like Galileo, Hebbia, τ-bench):
- Each dimension contains N binary check items
- Dimension score = passed / total (0.0–1.0)
- Weighted total = Σ(dimension score × weight) / Σ(weights)
- Weights reflect the dimension’s importance to workflow quality
Three Rubrics
eval has three built-in rubrics, corresponding to three profiles:comet-workflow rubric (9 dimensions)
Evaluates the classic five-phase workflow. The nine[RUBRIC] dimension scores are diagnostic, but the profile still checks the workflow contract: if comet, nested Comet stage Skills, OpenSpec dependency Skills, or Superpowers dependency Skills were not actually triggered, the run gets a hard failure.
generic rubric (7 dimensions)
Evaluates generic Skills. Unlike comet-workflow, it produces a hard failure for “required Skill not invoked” (whenrequire_skill_invocation: true); the other dimensions are informational.
authoring-skill rubric (11 dimensions)
Evaluates/comet-any generated Skill bundles. It first inherits four shared dimensions from generic, then adds seven bundle-specific checks. Missing SKILL.md, missing resolved-skills.json, missing workflow-protocol.json, missing Engine files, missing authoring-lanes.json, and missing skill-review.md all produce hard failures.
Weighted Total Formula
[RUBRIC] <dim>: <score> - <reason>, and finally a line [RUBRIC] weighted_score: <score>.
RubricAvg (report column)
TheRubricAvg column in the report is the simple mean (sum/len) of all dimension scores for that run (including the weighted_score row). Across multiple runs it’s the mean of each run’s mean. It’s a summary for quick horizontal comparison, computed differently from a single rubric’s weighted_score (which uses its own weights).
LLM-as-judge Override (Optional)
Enable by settingBENCH_LLM_JUDGE=1. By default, Comet’s final weighted_score is a rule score, not LLM-as-judge. Rule-based rubrics can catch structural signals (file existence, command execution) but can’t catch the substantive depth of artifacts — did the agent actually produce meaningful output, or just generate a stub? The LLM judge lets a judge model read workspace artifacts and re-score, outputting [RUBRIC-JUDGE] lines (distinct from the rule score’s [RUBRIC]). Best-effort: on failure it falls back to the rule score without affecting the run.
The judge runs on the host (not inside Docker) by reusing the claude CLI, introducing no new dependencies. Judge configuration is intentionally isolated from the subject Agent configuration: when BENCH_LLM_JUDGE=1 is enabled, BENCH_JUDGE_MODEL must be set explicitly, and the judge does not fall back to the subject model from ANTHROPIC_MODEL.
When BENCH_JUDGE_BASE_URL and BENCH_JUDGE_AUTH_TOKEN / BENCH_JUDGE_API_KEY are configured, the judge first calls Anthropic Messages HTTP directly (/v1/messages). Without a dedicated judge endpoint, it falls back to the host claude CLI, clears inherited subject ANTHROPIC_* provider settings, then maps the dedicated judge settings into its own CLI invocation:
This avoids the “same model as both contestant and judge” problem: the subject Agent can keep using its own
ANTHROPIC_MODEL, ANTHROPIC_BASE_URL, and token, while the judge must declare its model and provider separately. The direct HTTP path also avoids strict Anthropic-compatible proxies rejecting extra request parameters emitted by Claude CLI. If BENCH_LLM_JUDGE=1 is enabled without BENCH_JUDGE_MODEL, the report records:
enabled_and_successful, and the judge model is not invoked.
Different profiles cover different dimensions:
generic/authoring three-dimension scoring criteria:
When collecting workspace files, the judge skips
.git, node_modules, .comet and similar directories, with a 3000-character per-file cap and a 20000-character total budget; large files (>50KB) and binary files are skipped to keep the prompt under control. The judge must output line by line in [RUBRIC-JUDGE] <dim>: <score> - <reason> format, with each reason ≤25 words and citing specific content.
pass@k and pass^k
These two metrics measure capability vs reliability and are key to evaluating whether a Skill can run stably and repeatedly. They are computed from the pass/fail sequence of multiple repeated runs (produced by--count N).
Definitions
Where n = total runs, c = successful runs. “Success” = that run’s task validator had zero failures.
Why Both Are Needed

pass@k looks at the capability ceiling, pass^k looks at the reliability floor — the larger the gap, the more unstable
- High pass@k, low pass^k: capability is sufficient, but unstable — “can do it, but can’t guarantee doing it right every time”. For a Skill that users run repeatedly, this is a danger signal.
- Both high: can do it and gets it right every time — trustworthy.
How to Get Multiple Runs
--count N (a pytest option) repeats each (task, treatment) combination N times, producing N independent pass/fail results. The comparison report first filters to the analysis set: clear environment or runner noise is excluded, while flagged runs still participate in the main statistics and are called out. Those N booleans in the analysis set are the input to pass@k/pass^k.
How to Choose k
The report takes k values from{1, 2, 5} that don’t exceed the actual number of runs n (k is clamped to n), falling back to [1] when insufficient. Report columns: pass@1 [pass@2 pass@5] and pass^1 [pass^2 pass^5].
Where They Appear
pass@k/pass^k are not insummary.md but in the comparison report (produced by compare_baselines.py as comparison_report.md) in the ## pass@k / pass^k — capability vs reliability section. They are informational, not gates.
Dual-Agent Automatic Interaction Evaluation
For multi-stage workflow-type Skills (comet-workflow and authoring-skill profiles), the evaluation is completed by two Agents interacting automatically, with no human involvement needed.
Two Roles
Interaction Loop
The loop runs up tomax_turns outer round-trips (comet-workflow typically 12, authoring-skill typically 8), ending early when “complete” is hit (archive complete / workflow complete / all 5 phases, etc.).
Every subject Agent turn also runs with --output-format stream-json --verbose. The loop driver only concatenates the subject Agent’s stream-json stdout for the harness to parse; the user simulator Agent’s one-shot replies do not enter the main event stream. As a result, event statistics reflect the subject Agent’s observable behavior, not the simulator’s behavior.
max_turns is not the number of real internal working turns of the subject Agent, nor the number of tool calls. One outer round-trip means: the subject Agent runs to a decision point, the user simulator Agent replies, then the subject Agent continues with —resume. Within the same round-trip there may still be multiple assistant messages, tool calls, and file operations.Decision Point Detection
The subject Agent’s output text is judged as a decision point when it matches any of these signals (case-insensitive):--decision-pattern is also supported.
User Simulator Agent Instructions
The simulator Agent receives the simulator prompt + the subject Agent’s last message, and is asked to:- When asked to confirm, approve the proposed plan/name/approach
- When asked to choose, pick the most reasonable default
- Ask for clarification only when the question is genuinely ambiguous about “what to do”
- Never refuse; always let the workflow move forward
- Don’t write code or files
COMET_SIMULATOR_PROMPT; generic/authoring uses GENERIC_SIMULATOR_PROMPT (slightly simpler wording). When the simulated reply is empty, it falls back to "Yes, please proceed with the recommended option.".
Customizing the Simulator Prompt
The simulator instructions for auto_user evaluation can be overridden. By default it reads fromeval/simulator-instruction.md, whose content is the standard template of the principles above. Two override methods (from highest to lowest priority):
Relative paths for
BENCH_SIMULATOR_PROMPT_FILE are resolved from eval/, with a default value of simulator-instruction.md; the file is read only if it exists. This lets you swap in a different user behavior profile (e.g. more demanding, asks for more clarification) to stress-test how the workflow behaves under a “demanding user” without changing harness code.
Why It’s Designed This Way
Workflow-type Skills pause at decision points waiting for user confirmation. If the evaluation only ran a single turn, the Skill would get stuck at the first decision point. The dual-agent loop lets the evaluation automatically run the entire workflow (open→…→archive), while ensuring the user input at decision points is “reasonable and forward-moving” rather than hardcoded replies. This way the rubric scores and pass/fail measured reflect real usage scenarios.Single-turn vs Multi-turn
genericprofile (interaction.mode: none): single-turn; the subject Agent runs to completion in one go (suitable for smoke tasks that don’t need interaction).comet-workflow/authoring-skillprofile (interaction.mode: auto_user): multi-turn; enables the dual-agent loop.
comet-* or with category: comet are automatically inferred as the comet-workflow profile and enable auto_user.
The Three Axes of Evaluation: treatment × task × reps
An evaluation is the Cartesian product of three axes:treatment Enables A/B Comparison
Running multiple treatments on the same task lets you measure the marginal effect of the Comet Skill:
The comparison report (
compare_baselines.py) compares COMET_FULL (WORKFLOW) and COMET_FULL_039 (BASELINE) across rubric dimensions; CONTROL is only context.
How Metrics Get Into the Report
See Reading Evaluation Reports.
Next steps
- Reading Evaluation Reports — report structure and failure attribution
- Eval harness — collect/run internal mechanism and environment variables
- Evaluation System Overview — where eval fits in the publish workflow
- comet eval command — complete options reference

