Twelve percent is not a style
The first grader would have punished agents for fintech data and missed real violations. What a grader counts is a design decision.
Date: 2026-11-08. Context: extending the graders (feat/bench-graders).
What happened
The week 0 grader flagged any string in any object literal that looked like CSS:
#fff, 12px, 50%. It had three passing tests, and it looked done.
Before extending it, we ran it on three lines of the kind of data the fintech tasks will use:
{ id: "acc_1", balance: "48,210.00", change: "12%", color: "#2563eb" },
{ id: "acc_2", balance: "9,300.50", change: "-3.5%", apr: "4.25%" },
{ ref: "#a1b2c3", fee: "0.5%" }Plus a few lines of real styling mistakes. The result:
| Old grader | New grader | |
|---|---|---|
change: "12%", apr: "4.25%", fee: "0.5%", ref: "#a1b2c3" | 4 violations | none |
color: "white", <Box bg="#fff">, style={{ marginTop: 12 }} | missed | 3 violations |
bg: { _dark: "#0f172a" } | reported as property _dark | reported as bg |
In a fintech task, percentages are everywhere. The old grader would have charged the
agent for its data, and the number would have looked like design-system drift. It
also did not see named colors, JSX style props or bare numbers in style, which are
the most common shortcuts.
The fix is to grade only values that belong to a style property. A key counts when it
is a CSS property (camelCase or kebab-case), a Panda shorthand (bg, px, rounded),
or a condition (_hover, base, md) under one.
The new grader graded the design system itself next: 40 files, one hit. It was a real
raw value, minHeight: "100vh" in the Storybook decorator, which sits outside Panda's
include. It now carries a // vektos-ignore with the reason, the first one in the repo.
What we learned
A grader is a definition of "wrong", and a test suite only checks the cases you thought of. Three green tests said nothing about the data the benchmark will actually see. Running the grader on realistic input, and on code we know is right, found more than writing tests did.
The property filter has a blind spot of its own: a data key that happens to be a CSS
property. { width: "Width 12px" } is still flagged. { color: "#2563eb" } in a chart
series is flagged too, and that one is correct: a chart color should be a token.
What it means for Vektos
1. Every grader gets a reference run. The design system and the web app are graded
like agent code in grade.test.ts, and must come out clean. A false positive there
fails bun run check.
2. Graders run only on files the agent wrote. Task fixtures are JSON and are never graded. Data the agent writes inline is graded, and the property filter keeps it fair.
3. Report what a score means. "N raw values" is meaningful only next to the rule
that produced it. The Violation type carries rule and a message for that reason.
Follow-ups
- Done: property-aware grading, a shared
Violationtype, the reference run test. - When the five tasks exist, run every grader on a hand-written ideal solution for each task. It must score zero.