Skip to content

feat(skills): the expense-reporter skill and its eval cases - #30

Merged
IBJunior merged 3 commits into
mainfrom
feat/expense-reporter-skill
Sep 12, 2026
Merged

IBJunior merged 3 commits into
mainfrom
feat/expense-reporter-skill

Conversation

@IBJunior

Copy link
Copy Markdown
Member

Slices C1 and C2 of v3 phase 2 — the real skill, and the evals that grade it. Completes the phase: the loader (#26), the renderer (#28), the tool (#29), and now the instructions plus their guard.

The skill

Replaces the placeholder with four steps: whether to chart at all → which type → how to shape the query → how to read it back, plus what each of render_chart's refusals means.

The restraint half is the part that matters. Nothing in code stops the agent charting a single figure, and a skill that only encourages charts would make answers worse — so the skill says a single number is a sentence and a short ranking is a table.

The eval cases

eval/cases/skills.cases.mts — a defect class the suite did not cover: a capability the agent must choose to consult, and choose not to overuse. A unit test proves load_skill returns the body; only a model can show whether the agent reaches for it, reads it before acting, or leaves it alone.

Case Grades
chart-request-loads-skill activation
skill-read-before-charting order — toolCalledBefore(load_skill, render_chart)
trend-question-picks-line chart-type selection (a closed enum, so gradeable without a judge)
single-figure-needs-no-chart the over-triggering guard

The fixture needed no changes: the seeding formula already spreads expenses across nine months with varying totals, so the trend case has real shape and no second source of truth for expected figures.

What the evals found

A prompt defect. The skills section said "when a task matches a skill" without defining matching, and haiku fell back to lexical overlap — loading the skill for "show me a chart" but never for "chart my spending": 0/5, deterministic, identical trajectory every run. Sonnet loaded it either way, so the instruction was carrying the difference, not the model.

The fix states the principle (judge the KIND of work, not the vocabulary) and blocks the easy-task escape. Deliberately no example phrasings — listing synonyms would have made the eval pass by feeding it the answer. Activation went 0/5 → ~6/8 on haiku with no vocabulary shared between the prompt and the case.

A harness bug, older than this work. Only approval and config cases reset the fixture, so an approved log_expense left a $4.20 row behind and the next case asserting a Dining total read $2,737.20 against an expected $2,733.00. Three existing cases assert that same total and passed only because they run before the approval cases. Case order was load-bearing and silent.

A stale case. no-double-prompt-on-mutation claims "nothing legitimate to ask for", but the currency feature made that false — with no currency established the agent correctly asks before writing, so the case failed for the inverse of its own reason. Pre-seeding the currency makes the premise true again.

Verified

18/18 eval cases on claude-haiku-4-5 (2 legacy scripted twins still skipped), 185 unit tests, tsc and typecheck:eval clean.

🤖 Generated with Claude Code

IBJunior and others added 3 commits September 12, 2026 22:33
Replaces the placeholder body with real guidance: whether to chart at all,
which of the two types, how to shape the query, and how to read the result
back. The restraint half matters most — nothing in code stops the agent
charting a single figure, and a skill that encourages charts without saying
when to stop would make answers worse.

Adds eval/cases/skills.cases.mts, a defect class the suite did not cover:
a capability the agent must choose to consult, and choose not to overuse.
Four cases — activation, order, chart-type selection, and the
over-triggering guard, which is the one that keeps the rest honest.

The fixture already supports this: the seeding formula spreads expenses
across nine months with varying totals, so the trend case needed no new
seed data and no second source of truth for expected figures.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The skills section said "when a task matches a skill" without defining
what matching means, so a weaker model fell back to lexical overlap:
claude-haiku-4-5 loaded the expense-reporter skill for "show me a chart"
but never for "chart my spending" — 0/5, deterministic, same trajectory
every time. Sonnet loaded it either way, so the instruction was carrying
the difference.

Now states the principle — judge the KIND of work, not the vocabulary —
and blocks the easy-task escape, since the agent charted correctly
without the skill and had no reason to consult it.

Deliberately no example phrasings: listing the synonyms would have made
the eval pass by feeding it the answer, which tests the hint rather than
the reframe. Activation went 0/5 -> ~6/8 on haiku with no vocabulary
shared between the prompt and the case.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Case order was load-bearing and silent. Only `approval` and `config` cases
reset, so an approved log_expense left a $4.20 row behind and the next case
asserting a Dining TOTAL read fixture + leftovers — $2,737.20 against an
expected $2,733.00, identically across all three runs.

Three existing cases assert that same total and passed only because they
run before the approval cases in the barrel. The bug predates them being
noticed; a new case simply landed downstream of the mutation.

Also fixes no-double-prompt-on-mutation, whose premise went stale when the
currency feature shipped: it claims "nothing legitimate to ask for", but
with no currency established the agent correctly asks before writing, so
the case failed for the inverse of its own reason. Pre-seeding the currency
makes the premise true again.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@IBJunior
IBJunior merged commit 3a88298 into main Sep 12, 2026
1 check passed
@IBJunior
IBJunior deleted the feat/expense-reporter-skill branch September 12, 2026 20:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant