From 1cdc4084af1ba91f602ad1c9854ebc7f5e7dbb7c Mon Sep 17 00:00:00 2001 From: Christopher Tso Date: Fri, 19 Jun 2026 06:18:05 +0200 Subject: [PATCH 1/3] chore(evals): split self suites by plugin --- .../docs/getting-started/installation.mdx | 5 +- evals/README.md | 61 +- evals/{self => agentv-dev}/skills/README.md | 59 +- .../skills/fixtures/agentv-bench-full.txt | 3658 ++++++++++++----- .../skills/fixtures/agentv-bench.txt | 31 + .../fixtures/agentv-eval-review-full.txt | 294 ++ .../skills/fixtures/agentv-eval-review.txt | 53 + .../fixtures/agentv-eval-writer-full.txt | 86 +- .../skills/fixtures/agentv-eval-writer.txt | 0 .../fixtures/agentv-governance-full.txt | 342 +- .../skills/fixtures/agentv-governance.txt | 16 + .../fixtures/agentv-trace-analyst-full.txt | 16 + .../skills/fixtures/agentv-trace-analyst.txt | 16 + .../fixtures/skills-get-nonexistent.txt | 2 +- .../skills/fixtures/skills-list-all.txt | 1 + .../skills/output-correctness.eval.yaml | 8 +- .../skills/skill-invocation.eval.yaml | 2 + .../skills/skill-selection.eval.yaml | 30 +- evals/agentv-self/agentv-self.eval.yaml | 70 + .../azure-smoke.eval.yaml | 3 +- evals/agentv-self/scripts/setup.mjs | 41 + evals/self/eval.yaml | 87 - .../fixtures/agentv-eval-review-full.txt | 52 - .../skills/fixtures/agentv-eval-review.txt | 52 - .../fixtures/agentv-onboarding-full.txt | 63 - .../skills/fixtures/agentv-onboarding.txt | 63 - .../self/skills/fixtures/skills-list-all.txt | 1 - .../copilot-log-eval/workspace/AGENTS.md | 5 +- 28 files changed, 3400 insertions(+), 1717 deletions(-) rename evals/{self => agentv-dev}/skills/README.md (64%) rename evals/{self => agentv-dev}/skills/fixtures/agentv-bench-full.txt (55%) rename evals/{self => agentv-dev}/skills/fixtures/agentv-bench.txt (96%) create mode 100644 evals/agentv-dev/skills/fixtures/agentv-eval-review-full.txt create mode 100644 evals/agentv-dev/skills/fixtures/agentv-eval-review.txt rename evals/{self => agentv-dev}/skills/fixtures/agentv-eval-writer-full.txt (99%) rename evals/{self => agentv-dev}/skills/fixtures/agentv-eval-writer.txt (100%) rename evals/{self => agentv-dev}/skills/fixtures/agentv-governance-full.txt (98%) rename evals/{self => agentv-dev}/skills/fixtures/agentv-governance.txt (88%) rename evals/{self => agentv-dev}/skills/fixtures/agentv-trace-analyst-full.txt (93%) rename evals/{self => agentv-dev}/skills/fixtures/agentv-trace-analyst.txt (93%) rename evals/{self => agentv-dev}/skills/fixtures/skills-get-nonexistent.txt (60%) create mode 100644 evals/agentv-dev/skills/fixtures/skills-list-all.txt rename evals/{self => agentv-dev}/skills/output-correctness.eval.yaml (97%) rename evals/{self => agentv-dev}/skills/skill-invocation.eval.yaml (99%) rename evals/{self => agentv-dev}/skills/skill-selection.eval.yaml (83%) create mode 100644 evals/agentv-self/agentv-self.eval.yaml rename evals/{self => agentv-self}/azure-smoke.eval.yaml (74%) create mode 100644 evals/agentv-self/scripts/setup.mjs delete mode 100644 evals/self/eval.yaml delete mode 100644 evals/self/skills/fixtures/agentv-eval-review-full.txt delete mode 100644 evals/self/skills/fixtures/agentv-eval-review.txt delete mode 100644 evals/self/skills/fixtures/agentv-onboarding-full.txt delete mode 100644 evals/self/skills/fixtures/agentv-onboarding.txt delete mode 100644 evals/self/skills/fixtures/skills-list-all.txt diff --git a/apps/web/src/content/docs/docs/getting-started/installation.mdx b/apps/web/src/content/docs/docs/getting-started/installation.mdx index dca9211f9..5d3eec5f8 100644 --- a/apps/web/src/content/docs/docs/getting-started/installation.mdx +++ b/apps/web/src/content/docs/docs/getting-started/installation.mdx @@ -17,10 +17,11 @@ Install the AgentV CLI: npm install -g agentv ``` -Then load the onboarding skill and follow the instructions: +Then load a bundled skill and follow its instructions. For eval authoring, +`agentv-eval-writer` is the best starting point: ```bash -agentv skills get agentv-onboarding +agentv skills get agentv-eval-writer ``` Paste the output to your AI agent and ask it to set up AgentV in your repository. diff --git a/evals/README.md b/evals/README.md index 0c0c2bc27..efec5c6b3 100644 --- a/evals/README.md +++ b/evals/README.md @@ -1,36 +1,49 @@ -# AgentV Self-Evaluation +# AgentV Plugin-Aligned Self Evals -This folder contains evaluations to ensure AgentV's `AGENTS.md` design principles don't regress. +This directory holds the repo-owned self-evaluation suites, split to match the +current plugin boundary: + +- `agentv-self` covers AgentV's own repo guidance and self-eval workspace + behavior. +- `agentv-dev` covers the bundled `agentv skills` CLI surface and skill content + shipped with the developer plugin. ## Structure -``` +```text evals/ -├── targets.yaml # Target definitions -├── design-principles.yaml # Tests -└── design-principles-grader.md # LLM grader prompt +├── agentv-self/ +│ ├── agentv-self.eval.yaml +│ ├── azure-smoke.eval.yaml +│ └── scripts/setup.mjs +├── agentv-dev/ +│ └── skills/ +│ ├── *.eval.yaml +│ ├── fixtures/ +│ └── README.md +└── agentic-engineering/ ``` -## Known Issue: pi-coding-agent on Windows - -The pi-coding-agent provider has issues passing multi-line prompts on Windows. When running pi directly from CLI, it works correctly, but through the provider the prompt content is not received by pi. - -**Workaround**: Use mock provider for testing eval structure, or run on macOS/Linux. +## Running -**To fix**: Investigate `packages/core/src/evaluation/providers/pi-coding-agent.ts` - specifically how `spawn()` handles arguments with newlines on Windows. - -## Running the Evals +Use the local CLI source from the repo root: ```bash -# Dry run to verify structure -bun agentv eval evals/design-principles.yaml --targets evals/targets.yaml --dry-run - -# Full run (requires working pi-coding-agent or alternative target) -bun agentv eval evals/design-principles.yaml --targets evals/targets.yaml +# Validate the renamed suites +bun apps/cli/src/cli.ts validate evals/agentv-self/agentv-self.eval.yaml +bun apps/cli/src/cli.ts validate evals/agentv-self/azure-smoke.eval.yaml +bun apps/cli/src/cli.ts validate evals/agentv-dev/skills/*.eval.yaml + +# Prepare one agentv-self case and inspect the materialized workspace +bun apps/cli/src/cli.ts prepare \ + evals/agentv-self/agentv-self.eval.yaml \ + --test-id guidance-split-paths \ + --target codex + +# Run the agentv-dev skills suite against a target +bun apps/cli/src/cli.ts eval run evals/agentv-dev/skills/*.eval.yaml --target ``` -## Test Cases - -1. **violates-lightweight-core** - Proposes adding complex built-in (should fail) -2. **violates-ai-first-design** - Proposes rigid CLI instructions (should fail) -3. **follows-principles** - Proposes plugin/example approach (should pass) +`agentv-self/agentv-self.eval.yaml` uses a `before_all` hook to copy the current repo +checkout into the eval workspace. That keeps `/AGENTS.md` and `/.agents/` +current without declaring extra repos in workspace config. diff --git a/evals/self/skills/README.md b/evals/agentv-dev/skills/README.md similarity index 64% rename from evals/self/skills/README.md rename to evals/agentv-dev/skills/README.md index e476eeb9e..949334c2c 100644 --- a/evals/self/skills/README.md +++ b/evals/agentv-dev/skills/README.md @@ -1,9 +1,9 @@ -# `agentv skills` evals +# `agentv-dev` skill evals -Three-category eval suite covering the `agentv skills` CLI subcommand -(`list`, `get`, `path`) shipped in #1224. Each category isolates one -aspect of the skill UX so a regression in any single dimension is easy -to read off the run report. +Three-category eval suite covering the bundled `agentv skills` CLI surface +owned by the `agentv-dev` plugin (`list`, `get`, `path`). Each category +isolates one aspect of the skill UX so a regression in any single +dimension is easy to read off the run report. ## Categories @@ -19,16 +19,16 @@ skill evals: invocation (does the agent reach for the tool), selection ## Fixtures -`fixtures/` holds frozen snapshots of CLI output. They are checked in -so eval runs are deterministic and don't require network or build -state. Two flavours per skill: +`fixtures/` holds frozen snapshots of CLI output. They are checked in so +eval runs are deterministic and don't require network or build state. +Two flavours per skill: - `agentv-.txt` — bare `SKILL.md` content (`agentv skills get `). Used in most tests; small (1.5–25 KB). -- `agentv--full.txt` — same plus every file under `references/` - and `templates/` (`agentv skills get --full`). Only used in - tests that specifically validate `--full` behaviour, since these can - be hundreds of KB. +- `agentv--full.txt` — same plus any bundled support directories + (`agentv skills get --full`). Only used in tests that + specifically validate `--full` behaviour, since these can be hundreds + of KB. Plus two single-purpose fixtures: @@ -41,52 +41,49 @@ After any change to bundled skill content or the `agentv skills` CLI, regenerate fixtures from the worktree root: ```bash -cd evals/self/skills +cd evals/agentv-dev/skills # Bare SKILL.md per skill for skill in agentv-bench agentv-eval-review agentv-eval-writer \ - agentv-governance agentv-onboarding agentv-trace-analyst; do - node ../../../apps/cli/dist/cli.js skills get "$skill" \ + agentv-governance agentv-trace-analyst; do + bun ../../../apps/cli/src/cli.ts skills get "$skill" \ > "fixtures/${skill}.txt" 2>&1 done -# --full variants (with references/ + templates/) +# --full variants (with references/, scripts/, agents/, and similar) for skill in agentv-bench agentv-eval-review agentv-eval-writer \ - agentv-governance agentv-onboarding agentv-trace-analyst; do - node ../../../apps/cli/dist/cli.js skills get "$skill" --full \ + agentv-governance agentv-trace-analyst; do + bun ../../../apps/cli/src/cli.ts skills get "$skill" --full \ > "fixtures/${skill}-full.txt" 2>&1 done # Listing + error fixtures -node ../../../apps/cli/dist/cli.js skills list --json \ +bun ../../../apps/cli/src/cli.ts skills list --json \ > fixtures/skills-list-all.txt 2>&1 -node ../../../apps/cli/dist/cli.js skills get does-not-exist \ +bun ../../../apps/cli/src/cli.ts skills get does-not-exist \ > fixtures/skills-get-nonexistent.txt 2>&1 ``` -`bun apps/cli/src/cli.ts skills …` works equivalently when running -against TypeScript sources. - ## Running From the worktree root: ```bash # All three categories against one target -node apps/cli/dist/cli.js eval run evals/self/skills/*.eval.yaml --target +bun apps/cli/src/cli.ts eval run evals/agentv-dev/skills/*.eval.yaml --target # A single category -node apps/cli/dist/cli.js eval run \ - evals/self/skills/skill-selection.eval.yaml --target azure +bun apps/cli/src/cli.ts eval run \ + evals/agentv-dev/skills/skill-selection.eval.yaml --target azure # A single test -node apps/cli/dist/cli.js eval run \ - evals/self/skills/skill-invocation.eval.yaml \ +bun apps/cli/src/cli.ts eval run \ + evals/agentv-dev/skills/skill-invocation.eval.yaml \ --test-id invoke-get-full-flag --target azure ``` -`` is any name resolvable from `targets.yaml` in the worktree -(e.g. `azure`, `claude`, `mock`). +`` is any name resolvable from `.agentv/targets.yaml` in the worktree +(for example `azure`, `claude`, or `codex`). ## Adding test cases @@ -110,7 +107,7 @@ Pattern for fixture-driven tests: - role: user content: - type: file - value: fixtures/agentv-onboarding.txt + value: fixtures/agentv-eval-writer.txt - type: text value: | diff --git a/evals/self/skills/fixtures/agentv-bench-full.txt b/evals/agentv-dev/skills/fixtures/agentv-bench-full.txt similarity index 55% rename from evals/self/skills/fixtures/agentv-bench-full.txt rename to evals/agentv-dev/skills/fixtures/agentv-bench-full.txt index 29ef810f0..36d962d71 100644 --- a/evals/self/skills/fixtures/agentv-bench-full.txt +++ b/evals/agentv-dev/skills/fixtures/agentv-bench-full.txt @@ -175,6 +175,21 @@ When each subagent task completes, you receive a notification containing `total_ This is the only opportunity to capture this data — it comes through the task notification and isn't persisted elsewhere. Process each notification as it arrives. +### After all executors complete + +Once all executor subagent notifications arrive, **read `response.md` directly from disk**. +Do NOT use `read_agent` to fetch executor results — they are already written to the run +directory. Verify all responses exist before proceeding: + +```bash +for d in //*/; do + echo "$(basename $d): $(ls "$d"/response.md 2>/dev/null && echo OK || echo MISSING)" +done +``` + +If any `response.md` is MISSING, re-run that specific executor subagent. Do not proceed to +grading until all responses are present. + ### Grading **In CLI mode**, `agentv eval` handles all grading end-to-end — no manual phases needed. @@ -427,1512 +442,2881 @@ Repeating the core loop for emphasis: Take your time with improvements. Read the transcripts. Understand why failures happened. Make changes that generalize beyond the test set. This is important work. ---- references/autoresearch.md --- -# Autoresearch Mode - -Autoresearch is an unattended eval-improve loop that runs multiple optimize cycles without human intervention. The user triggers it with natural language (e.g., "run autoresearch on this skill", "optimize this skill unattended"). No YAML schema changes or CLI flags are needed. - -## Automated Keep/Discard +## Accessing reference files -After each iteration, you can automatically decide whether to keep or discard the change using structured comparison output. This replaces manual judgment at steps 3–4 of the iteration loop (Step 5 in SKILL.md), except at human checkpoint iterations (3, 6, 9) where you must still present results to the user. +To load a specific reference without pulling the entire skill into context: -### 1. Run the comparison +```bash +agentv skills get agentv-bench --ref eval-yaml-spec +``` -After re-running test cases, compare the new results against the previous iteration's baseline: +Or resolve the skill directory and read files directly: ```bash -agentv compare .jsonl .jsonl --json +cat $(agentv skills path agentv-bench)/references/eval-yaml-spec.md ``` -Where `.jsonl` is the `index.jsonl` from the previous best iteration and `.jsonl` is the `index.jsonl` from the run you just completed. +Use `--full` to retrieve every file in the skill at once. -### 2. Parse the output +--- agents/executor.md --- +--- +name: executor +description: >- + Execute an AgentV evaluation test case by performing the task described in the + input. Reads input.json from the test directory, carries out the task using + available tools, and writes response.md with the result. Dispatch one executor + subagent per test case, all in parallel. +model: inherit +color: cyan +--- -The `--json` flag produces structured output: +You are the executor for an AgentV evaluation test case. Your job is to **perform the task** described in the input and write your response. + +You are the target agent being evaluated. Do the task to the best of your ability — your output will be graded by a separate grader agent. + +**You will receive these parameters:** +- `test-dir`: Path to the test case directory (e.g., `.agentv/results/runs///`) + +## Process + +1. **Read `{test-dir}/input.json`**. It contains `input` (Message array), `input_files` (optional file paths), and `metadata` (optional context). If `input_files` are listed, read those files too. + +2. **Perform the task** described in the input. + +3. **Write `{test-dir}/response.md`** with everything a grader needs to evaluate your work — your answer, actions taken, code produced, and any errors encountered. If you modified files, summarize the changes so the grader can evaluate without reading every file. + +## Important + +- Do NOT read grading criteria, assertions, or expected outputs — those are for the grader, not for you. +- Write `response.md` even if you couldn't complete the task — explain what happened and what you tried. + +--- agents/comparator.md --- +--- +name: comparator +description: >- + Perform bias-free blind comparison of evaluation outputs from multiple providers + or configurations. Randomizes labeling, generates task-specific rubrics, scores + N-way comparisons, then unblinds results and attributes improvements. Dispatch + this agent when comparing outputs across targets or iterations. +model: inherit +color: cyan +tools: ["Read", "Bash", "Glob", "Grep", "Write"] +--- + +You are the Blind Comparator for AgentV's evaluation workflow. Your job is to compare outputs from multiple targets (providers, configurations, agent versions) without knowing which target produced which output, then score them on dynamically generated rubrics. + +## Core Principles + +1. **Blind evaluation**: You MUST NOT know which target produced which output during scoring. Outputs are labeled A, B, C, ... only. +2. **Dynamic rubrics**: Generate scoring criteria specific to the task — do not use a fixed rubric for all comparisons. +3. **Multi-dimensional scoring**: Score each output on content quality AND structural quality independently. +4. **N-way support**: Handle 2 or more outputs, not just binary A/B. + +## Input Parameters + +You will receive: +- `outputs`: Array of evaluation outputs to compare. Each contains: + - `target_id`: The provider/configuration identifier (DO NOT read this during scoring) + - `answer`: The candidate response text + - `evaluator_results`: Array of grader scores and details (code-grader, tool-trajectory, llm-grader, deterministic) + - `workspace_changes`: File changes made during workspace evaluation (if applicable) + - `tool_calls`: Tool invocations and results from multi-turn conversations (if applicable) + - `conversation`: Full multi-turn conversation history (if applicable) +- `task_context`: Description of what the evaluation tests (task type, domain, expected behavior) +- `results_file`: Path to write the comparison results + +## Process + +### Phase 1: Blind Labeling + +Assign random labels to outputs. Use the following procedure: + +1. Collect all outputs into an array +2. Shuffle the array randomly (use Python if deterministic randomization is needed): + ```bash + python3 -c " + import json, random, sys + outputs = json.loads(sys.stdin.read()) + random.shuffle(outputs) + labels = [chr(65 + i) for i in range(len(outputs))] # A, B, C, ... + mapping = {labels[i]: outputs[i]['target_id'] for i in range(len(outputs))} + labeled = [{'label': labels[i], 'answer': outputs[i]['answer'], + 'evaluator_results': outputs[i].get('evaluator_results', []), + 'workspace_changes': outputs[i].get('workspace_changes', []), + 'tool_calls': outputs[i].get('tool_calls', []), + 'conversation': outputs[i].get('conversation', [])} + for i in range(len(outputs))] + print(json.dumps({'labeled': labeled, 'mapping': mapping})) + " <<< '' + ``` +3. Store the label→target mapping but DO NOT reference it until Phase 4 +4. Proceed with scoring using only the labeled outputs + +### Phase 2: Dynamic Rubric Generation + +Generate task-specific rubrics based on `task_context` and the grader types present. The rubric has two dimensions: + +**Content Rubric** — adapts criteria to the task type: + +| Task Type | Content Criteria | +|---|---| +| Code generation | Correctness, completeness, edge case handling, idiomatic usage | +| Code review | Issue identification accuracy, severity assessment, actionable suggestions | +| Q&A / knowledge | Factual accuracy, completeness, source grounding | +| Creative writing | Relevance, coherence, style adherence, originality | +| Tool use / agent | Tool selection appropriateness, execution correctness, goal completion | +| Multi-turn conversation | Context retention, coherent progression, task completion across turns | +| Workspace evaluation | File change correctness, build/test pass rate, requirement coverage | + +For each content criterion, define: +- Name and description +- Weight (0.0–1.0, sum to 1.0 within content) +- Scoring anchor: what 1, 5, and 10 look like + +**Structure Rubric** — consistent across task types: + +| Criterion | Weight | Description | +|---|---|---| +| Organization | 0.3 | Logical flow, section structure, progressive disclosure | +| Clarity | 0.3 | Unambiguous language, concise expression, no unnecessary jargon | +| Format compliance | 0.2 | Adherence to requested output format (JSON, markdown, code blocks) | +| Completeness | 0.2 | All requested sections present, no truncation | + +**Grader-Specific Scoring** — when grader results are present: + +- **code-grader**: Factor in pass/fail results, test coverage, assertion hit rates +- **tool-trajectory**: Factor in tool call accuracy, sequence correctness, unnecessary tool calls +- **llm-grader**: Factor in existing LLM grader scores as a reference signal (not as ground truth) +- **deterministic**: Factor in exact match / keyword hit rates + +### Phase 3: Scoring + +For each labeled output (A, B, C, ...): + +1. **Content score** (1–10): Apply the content rubric criteria with weights +2. **Structure score** (1–10): Apply the structure rubric criteria with weights +3. **Grader score** (1–10): Normalize grader results to a 1–10 scale. If no grader results, omit this dimension. +4. **Overall score**: Weighted combination: + - If grader results present: `0.5 × content + 0.2 × structure + 0.3 × grader` + - If no grader results: `0.7 × content + 0.3 × structure` + +For N > 2 outputs, use **round-robin pairwise comparison** to establish ranking: +- Compare every pair (A vs B, A vs C, B vs C, ...) +- Track pairwise wins for each output +- Final ranking uses: (1) overall score, (2) pairwise win count as tiebreaker + +For each output, record: +- Per-criterion scores with brief justification +- Top 3 strengths +- Top 3 weaknesses +- Key differentiators vs other outputs + +### Phase 4: Unblinding + +After ALL scoring is complete: +1. Reveal the label→target mapping +2. Associate scores with actual target identifiers +3. Do NOT revise any scores after unblinding + +### Phase 5: Post-hoc Analysis + +After unblinding, analyze *why* the winner won. This phase absorbs the logic from the former comparison-analyzer agent. + +1. **Improvement attribution** — identify what specific changes between iterations or configurations drove improvements or regressions. Quote from the outputs. +2. **Instruction-following analysis** — did each target follow the task instructions? Score 1-10 with specific issues noted. +3. **Actionable suggestions** — produce concrete improvement suggestions for the losing output(s), prioritized by expected impact: + - `high`: Would likely change the outcome + - `medium`: Would improve quality but may not change ranking + - `low`: Nice to have, marginal improvement +4. **Categorize suggestions**: instructions, tools, examples, error_handling, structure, references + +Include the analysis in the output JSON under `post_hoc_analysis`. + +## Output Format + +Write the comparison results to `results_file` as JSON: ```json { - "summary": { - "wins": 3, - "losses": 1, - "ties": 6, - "mean_delta": 0.05 + "comparison_id": "-", + "task_context": "", + "output_count": , + "rubric": { + "content": { + "criteria": [ + {"name": "", "weight": <0.0-1.0>, "description": ""} + ] + }, + "structure": { + "criteria": [ + {"name": "", "weight": <0.0-1.0>, "description": ""} + ] + }, + "overall_weights": { + "content": , + "structure": , + "grader": + } + }, + "results": [ + { + "label": "A", + "target_id": "", + "scores": { + "content": <1-10>, + "structure": <1-10>, + "grader": <1-10 or null>, + "overall": <1-10> + }, + "content_breakdown": [ + {"criterion": "", "score": <1-10>, "justification": ""} + ], + "structure_breakdown": [ + {"criterion": "", "score": <1-10>, "justification": ""} + ], + "evaluator_breakdown": [ + {"evaluator_name": "", "type": "", "raw_score": <0.0-1.0>, "normalized": <1-10>} + ], + "strengths": ["", "", ""], + "weaknesses": ["", "", ""] + } + ], + "pairwise": [ + {"pair": ["A", "B"], "winner": "A", "margin": } + ], + "ranking": [ + {"rank": 1, "label": "A", "target_id": "", "overall_score": , "pairwise_wins": } + ], + "winner": { + "label": "", + "target_id": "", + "overall_score": , + "margin_over_second": } } ``` -- **wins**: number of test cases where the candidate scored higher than the baseline -- **losses**: number of test cases where the candidate scored lower -- **ties**: number of test cases with no score change -- **mean_delta**: average score difference across all test cases (positive = candidate is better) - -### 3. Apply decision rules +Also produce a human-readable markdown summary: -Use these rules in order: +```markdown +## Blind Comparison Results -| Condition | Decision | Action | -|-----------|----------|--------| -| `wins > losses` | **KEEP** | Promote the candidate to the new baseline. Copy or note its `index.jsonl` path as the baseline for the next iteration. | -| `wins <= losses` | **DISCARD** | Revert the prompt/skill/config change. The previous baseline remains. Try a different mutation on the next iteration. | -| `mean_delta == 0` AND candidate prompt is shorter (fewer lines) | **KEEP** | Simpler prompts are preferred when performance is equal. Promote the candidate as the new baseline. | +### Task + -When `mean_delta == 0` and the candidate prompt is *not* shorter, treat it as a **DISCARD** — there's no reason to keep a change that adds complexity without improving results. +### Rubric + -### 4. Log the decision +### Rankings +| Rank | Label | Target | Overall | Content | Structure | Grader | +|------|-------|--------|---------|---------|-----------|-----------| +| 1 | A | | 8.5 | 9.0 | 7.5 | 8.5 | -Before proceeding to the next iteration, log the decision and rationale so the user can review later: +### Winner: