🔬 De skill die een sterk model schaadde
deel/part 1 🧩 idiomen vouwen fold + measure deel/part 2 🪤 de val train-on-test deel/part 3 📉 de meting skill hurt opus deel/part 4 🔧 de fix regression gone
Het onderzoek in vier delen · The investigation in four parts.

We hebben onze eigen agent gemeten — en de skill die hem moest helpen, schaadde hem

Agent-eval "lab-metr'd", 17 juli 2026. Een verhaal in vier delen, met een eerlijke correctie op mezelf als rode draad.

Kort de opzet: we bouwen een meter die onze eigen AI-agenten test — hier: kan een model correcte STAR-CCM+ 21.02 macro's schrijven? En we hebben een curated skill (14 KB aan geverifieerde API-idiomen) die een zwak model enorm helpt (lokale Qwen ging van 8% naar 83%). De vraag van deze sessie: helpt diezelfde skill ook een sterk model (opus)?

Deel 1 — De opzet, en de eerste illusie

We vouwden 6 geverifieerde idiomen in de skill (dat deel is af en klopt). Toen wilde ik meten of de skill opus omhoog tilt. Eerste meting leek positief. Maar een onafhankelijke review van het ontwerp — vóór ik iets rapporteerde — ving een fatale fout.

de EVAL (de toets) de SKILL (het lesmateriaal) idiomen gemijnd uit faal-logs skill getest op dezelfde eval → circulair: de skill bevatte het antwoordblad
Train-on-test. De idiomen kwamen uit de faal-logs van deze eval — dus de skill bevatte de antwoorden op 6 à 7 van de 12 taken. Elke "winst" was kopiëren, geen leren.

Ik startte zelfs een dure herhaling — onder druk van de "blijf-doorgaan"-teller — zonder te checken waar de idiomen vandaan kwamen. Een controle van 30 seconden had het gevangen. Ik stopte de besmette run.

Deel 2 — De schone test bouwen

Om het écht te meten heb ik twee taken gekozen waarvan de klassen aantoonbaar niet in de skill staan: block (heeft star.meshing nodig) en section (heeft star.vis.PlaneSection nodig). Als de skill echt algemene discipline leert, zou hij ook hier moeten helpen. En ik bouwde een correctheids-oracle: de macro moet het object echt maken, benoemen met een verzonnen naam (TransferPlane42), en die naam printen — dus "compileert toevallig" telt niet.

Deze test is niet besmet: de skill heeft deze antwoorden niet. Wat hij hier doet, is puur transfer.

Deel 3 — De meting: de skill schaadde opus (gereproduceerd)

Twee onafhankelijke batches, 8 samples per taak. Op de section-prompt was het resultaat identiek in beide runs:

section-prompt: geslaagde macro's (gepoold, 16 samples) 14/16 controle geen skill · 88% 4/16 met skill 25% 0
Run 1: 7/8 → 2/8. Run 2: 7/8 → 2/8. Identiek. Binnen-prompt Fisher p≈0,001. block liet géén effect zien (ruis, beide keren gelijk).

Waarom? Dit is de kern. Zonder skill gebruikt opus een veilige wildcard-import (import star.vis.*) en vindt de klasse. De skill zegt: "importeer alleen de symbolen die je echt gebruikt" — dat duwt opus naar smalle, expliciete imports die hij dán fout gokt.

controle (geen skill) schrijft import star.vis.*; (brede wildcard, veilig) → PlaneSection gevonden ✓ SLAAGT met skill skill-regel 206: "importeer alleen wat je gebruikt" → gaat expliciet gokken: import star.common.PlaneSection; → fout package (moet star.vis zijn) → ✗ FAALT
Import-anchoring. De skill strip't opus' betrouwbare default. Bij block onschadelijk (star.meshing is te raden); bij section fataal (star.vis is níet te raden — opus gokt star.common).

Eerlijke grenzen. Dit is één prompt, geen taak-familie — de twee runs hersamplen dezelfde input, dus het bewijst dat de bias stabiel is, niet algemeen. Netto over de 2 geteste prompts is het gemengd (section geschaad, block nul). Eén model, één eval. Echt en reproduceerbaar, maar smal: "een grote altijd-geïnjecteerde skill kán een sterk model scherp schaden op mínstens één klasse die hij niet dekt."

De rode draad: ik overdrijf mijn eigen resultaten

4× in één sessie: mijn eerste lezing → onafhankelijke check → smallere waarheid 1 · "de skill helpt" → check → train-on-test, meet niets 2 · "+3 is ruis" → check → rekenfout, het wáren de fixes 3 · "skill laat star.vis weg" → check → fout páckage, niet weglaten 4 · "twee losse runs" → check → één prompt, hersampled
Elke eerste lezing overclaimde; elke onafhankelijke check maakte het smaller en waarder. Maker ≠ controleur was elke keer dragend.

De bruikbaarste conclusie voor jou: weeg mijn eerste lezing van elk geconstrueerd resultaat als voorlopig, tot een onafhankelijke check heeft gedraaid. De "blijf-doorgaan"-teller die om een onafhankelijk oordeel bleef vragen, had elke keer gelijk.

Wat het betekent — en de knop die jij hebt

De gevouwen idiomen (+ block/section/stopsteps) zijn geverifieerd-correcte API — vouwen verbetert de correctheid van de skill. De skill helpt een zwak model (Qwen). Voor een sterk model tilt hij de score niet, en kán hij op ongedekte klassen juist schaden.

Concrete fix — inmiddels gedaan + geverifieerd (zie Deel 4): de import-regel is versoepeld naar wildcard-voorkeur, plus "vertrouw je eigen kennis voor niet-genoemde klassen". De regressie is weg, de kerntaken intact.

Deel 4 — En toen hebben we het gefixt (geverifieerd)

De oorzaak zat niet in de gevouwen idiomen (die bevatten nul import-regels) maar in een al-bestaande regel: "importeer alleen symbolen die je gebruikt", plus de zware nadruk op star.common. Twee aanpassingen aan de skill:

1. Prefereer wildcard-imports (import star.vis.*;) — die kunnen nooit het verkeerde package gokken.

2. Voor elke klasse die de skill níet noemt: vertrouw je eigen kennis en default niet naar star.common (section/scene/derived-part → star.vis).

de fout die de regressie veroorzaakte: verkeerd star.common.PlaneSection 6/8 oude skill 0/8 gefixte skill de foute gok is helemaal weg → section herstelde 2/8 → 7/8 (= control)
Het robuuste bewijs is dit mechanisme (6/8 → 0/8 foute package-gok), niet de losse pass-score.

Geverifieerd, beide helften: de anker-regressie is weg (mechanisme 6/8 → 0/8) én de gevouwen idiomen bleven werken (11/12 onder de gesoftende skill). Vs je skill van vóór deze sessie: +6 geverifieerde idiomen, veiligere wildcard-imports, regressie eruit. Geïntegreerd strikt beter.

Eerlijk: de fix werkt door de star.vis-familie te dekken (ik noem hem nu expliciet), niet door bewezen algemene transfer. En de 7/8-score is één run met ongepinde temperatuur (ruizig) — daarom is de kop het 0/8-mechanisme, niet de score.

Nog open (jouw go): block/section in de skill vouwen · een multi-prompt run met vaste temperatuur om algemeenheid te toetsen (metered API = kleine kosten).

Waarom dit foundational is

We measured our own agent — and the skill meant to help it, hurt it

Agent-eval "lab-metr'd", 17 July 2026. A story in four parts, with an honest self-correction running through it.

The setup, briefly: we build a meter that tests our own AI agents — here, can a model write correct STAR-CCM+ 21.02 macros? And we have a curated skill (14 KB of verified API idioms) that hugely helps a weak model (local Qwen went 8% → 83%). This session's question: does that same skill also help a strong model (opus)?

Part 1 — The setup, and the first illusion

We folded 6 verified idioms into the skill (that part is done and correct). Then I wanted to measure whether the skill lifts opus. The first measurement looked positive. But an independent review of the design — before I reported anything — caught a fatal flaw.

the EVAL (the exam) the SKILL (the study material) idioms mined from failure logs skill tested on the same eval → circular: the skill held the answer key
Train-on-test. The idioms came from this eval's failure logs — so the skill contained the answers to 6–7 of the 12 tasks. Any "win" was copying, not learning.

I even launched an expensive replication — under the "keep going" counter's pressure — without checking where the idioms came from. A 30-second check would have caught it. I stopped the contaminated run.

Part 2 — Building the clean test

To measure it for real I picked two tasks whose classes are provably absent from the skill: block (needs star.meshing) and section (needs star.vis.PlaneSection). If the skill teaches general discipline, it should help here too. And I built a correctness oracle: the macro must actually create the object, name it with an invented name (TransferPlane42), and print that name — so "happens to compile" doesn't count.

This test is uncontaminated: the skill doesn't hold these answers. What it does here is pure transfer.

Part 3 — The reading: the skill hurt opus (reproduced)

Two independent batches, 8 samples per task. On the section prompt the result was identical across both runs:

section prompt: macros that succeeded (pooled, 16 samples) 14/16 control no skill · 88% 4/16 with skill 25% 0
Run 1: 7/8 → 2/8. Run 2: 7/8 → 2/8. Identical. Within-prompt Fisher p≈0.001. block showed no effect (noise, equal both times).

Why? This is the heart of it. Without the skill, opus uses a safe wildcard import (import star.vis.*) and finds the class. The skill says: "import only the symbols you actually use" — which pushes opus toward narrow, explicit imports that it then guesses wrong.

control (no skill) writes import star.vis.*; (broad wildcard, safe) → PlaneSection found ✓ PASS with skill skill line 206: "import only what you use" → guesses explicitly: import star.common.PlaneSection; → wrong package (should be star.vis) → ✗ FAIL
Import-anchoring. The skill strips opus's reliable default. For block it's harmless (star.meshing is guessable); for section it's fatal (star.vis is not — opus guesses star.common).

Honest bounds. This is one prompt, not a task family — the two runs re-sample the same input, so it proves the bias is stable, not general. Net across the 2 prompts tested is mixed (section hurt, block null). One model, one eval. Real and reproducible, but narrow: "a big always-injected skill can sharply hurt a strong model on at least one class it doesn't cover."

The through-line: I over-frame my own results

4× in one session: my first read → independent check → narrower truth 1 · "the skill helps" → check → train-on-test, measures nothing 2 · "+3 is noise" → check → arithmetic backwards, it was the fixes 3 · "skill omits star.vis" → check → wrong pÁckage, not omission 4 · "two independent runs" → check → one prompt, re-sampled
Every first read over-claimed; every independent check made it narrower and truer. Creator ≠ validator was load-bearing each time.

The most useful takeaway for you: weight my first read of any constructed result as provisional until an independent check has run. The "keep going" counter that kept demanding an independent verdict was right every time.

What it means — and the knob you hold

The folded idioms (+ block/section/stopsteps) are verified-correct API — folding improves the skill's correctness. The skill helps a weak model (Qwen). For a strong model it doesn't lift the score, and can actively hurt on uncovered classes.

Concrete fix — now done + verified (see Part 4): the import rule is softened to wildcard-preference, plus "trust your own knowledge for unlisted classes". The regression is gone, the core tasks intact.

Part 4 — And then we fixed it (verified)

The cause was not the folded idioms (they contain zero import rules) but a pre-existing line: "import only symbols you actually use", plus the heavy emphasis on star.common. Two edits to the skill:

1. Prefer wildcard imports (import star.vis.*;) — they can never guess the wrong package.

2. For any class the skill does not list: trust your own knowledge and do not default to star.common (section/scene/derived-part → star.vis).

the error that caused the regression: wrong star.common.PlaneSection 6/8 old skill 0/8 fixed skill the wrong guess is gone entirely → section recovered 2/8 → 7/8 (= control)
The robust evidence is this mechanism (6/8 → 0/8 wrong-package guesses), not the single pass count.

Verified, both halves: the anchoring regression is gone (mechanism 6/8 → 0/8) and the folded idioms still work (11/12 under the softened skill). Vs the pre-session skill: +6 verified idioms, safer wildcard imports, regression removed. Integrated strictly better.

Honest: the fix works by covering the star.vis family (it now names it), not by demonstrated general transfer. And the 7/8 count is a single unpinned-temperature run (noisy) — so the headline is the 0/8 mechanism, not the count.

Still open (your go): fold block/section into the skill · a multi-prompt, pinned-temperature run to test generality (metered API = a small cost).

Why this is foundational


bron / source agent_eval/STATUS.md · idiomen: CANDIDATE_IDIOMS_FOR_DINO.md · les: gotcha_skill_eval_train_on_test