Jev in n8n and design systems
Something gets made, by an agent or a person, and a small call decides what happens next: which n8n branch, which component, whether a button breaks a rule. Jev makes those calls in about 200 ms and says how sure it is, so a person or a bigger model only steps in when it isn't.
Where it fits
Jev doesn't write code. It answers small multiple-choice questions about things that already exist, so it handles the decisions around the model that does:
| When | The question | Type |
|---|---|---|
| Before generating | Which existing component should this frame become? | choice over the kit |
| While generating | Which variant, token or icon fits this spot? | choice |
| After generating | Does this break a rule in DESIGN.md? | noul per rule |
| In n8n | Which branch does this item take? | choice, feeding a Switch |
Swapping the LLM classifier for one HTTP node
The most common AI pattern in n8n's templates is trigger, LLM classifier, parse JSON, Switch. In "Classify and route DevOps chat requests with Mattermost and Claude", the classifier is a Sonnet agent with a 5,280-character prompt, then an 85-line Code node to dig the JSON out and validate it.
Jev uses the template's 7 categories as its options. On 28 new DevOps messages:
| Right | Median time | Cost for all 28 | |
|---|---|---|---|
| The template's Sonnet classifier | 28 / 28 | 2.1 s | $0.151 |
| One Jev choice | 28 / 28 | 180 ms | $0.0014 |
Same answers, about 106× cheaper and 12× faster, with no parser. Jev's three least confident answers were the three truly borderline messages, which is where an "Ask a human" branch belongs:
- “how do I get read access to the prod replica?”:
questionat 0.54 - “Let's move our secrets from env files to Vault properly, we've never had it”:
new_systemat 0.66 - “the release workflow can't push the image, 403 from the registry”:
ci_cd_errorat 0.89
A workflow you can import
design/n8n/jev-router.json imports straight into n8n: Webhook, an HTTP Request to Jev, then an If on confidence ≥ 0.6 before the Switch, with low-confidence items going to "Ask a human". The Jev node takes about 247 ms; for a deploy stuck waiting on a runner it returns ci_cd_error at 0.95.
// all the "AI" in the workflow is this one HTTP Request node
POST https://openrouter.ai/api/v1/systemone
{ "model": "~typesafe/jev-latest",
"state": { "message": "{{ $json.message }}" },
"questions": { "category": { "type": "choice", "criteria": { …the template's categories… } } } }
// Switch on {{ $json.answers.category.choice }}; If on .confidence
n8n's built-in Text Classifier does the same with any chat model, but without a confidence, so unclear items can only be dropped or sent to "Other".
Linting the rules a regex can't check
The app's DESIGN.md says "Do not tip menu triggers" and "Do not tip close / dismiss X buttons". A regex can't tell what a button does, but two yes/no questions per <Tip> can. Across all 53 Tips:
| Tooltip | Rule it breaks | Jev | Where |
|---|---|---|---|
| Clear | close / dismiss | 0.92 | keybind-settings.tsx |
| Voice chat engine | opens a menu | 0.92 | start-voice-button.tsx |
| New… | opens a menu | 0.94 | roster-pane-toolbar.tsx |
| Clear search | close / dismiss | 0.95 | search-field.tsx |
All 4 are real violations. "Clear search" lives in SearchField, so every search box inherits it. A fuzzier question, "does hovering teach something new?", gave muddy scores because it depends on surrounding context. Keep the linter to precise questions.
In a workflow it runs after the coding agent, one call per changed Tip, and anything at 0.8+ becomes a review comment quoting the rule.
Does the icon say what the label says?
DESIGN.md asks you to "reuse the existing icon for an action." One noul per icon-only button asks whether the icon says what its label says. 12 of 33 pairs score 0.8 or above. The lowest:
| Icon | Label | Fits | Jev would pick | Where |
|---|---|---|---|---|
discard | Restore checkpoint | 0.06 | refresh | user-message.tsx |
kebab-vertical | New branch | 0.09 | add | coding-row.tsx |
watch | Manage | 0.22 | watch | cron-jobs-section.tsx |
discard | Reset to default | 0.28 | refresh | keybind-settings.tsx |
discard | Reset HUD size and position | 0.28 | screen-normal | controls.tsx |
add | Stage all | 0.30 | check-all | index.tsx |
These are review candidates, not bugs. The clearest is "New branch" on a kebab icon, which says "more". The discard rows are a naming question: the app uses discard for revert and reset, where Jev expects refresh. One line in DESIGN.md would settle it.
Which Button variant goes here?
A design-to-code agent picks <Button variant="…"> constantly. Here are 166 Buttons from the app with variant removed, put to one choice over the 8 variants, described in button.tsx's own words.
| Confidence | Buttons | Matched the code |
|---|---|---|
| 0.9 or higher | 37 | 95% |
| 0.6 to 0.9 | 33 | 30% |
| below 0.6 | 96 | 21% |
Only 39% match overall, since ghost versus text depends on context the snippet lacks. But confident picks are almost always right (37 at 95%), so take those and let the agent check neighbours otherwise.
Building on the component that already exists
Design systems drift when someone hand-builds a copy of an existing component. For 26 commits that cleaned this up, the removed markup is the state and the 45 components/ui primitives are the options.
Jev's first pick matches the commit 7 of 26 times, its top three 10 of 26, and at 0.9+ it's right 7 of 11:
- scope pane fallback menus to the clicked content:
actions-menuat 0.92 - every settings row renders through one ListRow/ToggleRow/Slider/SearchField:
search-fieldat 0.97 - one identity chip behind every avatar:
avatar-chipat 0.93 - let ConfirmDialog carry a secondary action:
confirm-dialogat 1.00 - route cron delete and review revert through ConfirmDialog:
confirm-dialogat 1.00
So it's a hint, not a gate. It reliably catches rebuilt ConfirmDialogs, AvatarChips and SearchFields, the most common kind, and misses when the answer depends on purpose rather than markup. Figma frames share that limit, so send the frame name and notes too.
Figma: the Dev Mode MCP's layer tree would go in state, with the kit as options. Untested: it needs the desktop app with a file open.
Putting it together
A design-to-code workflow with Jev at each decision:
Trigger: Figma frame / PR opened
└─ Gate · Jev choice which kit components? (≥0.9: pass them as hints)
└─ Agent writes the code, using those components
└─ Gate · Jev noul × DESIGN.md rules tip on a dismiss button? hand-built dialog?
├─ all clear: Agent opens the PR
├─ confident violation: Agent fixes it and loops back
└─ unsure: Human, a designer reviews the flagged spots
What to build first:
- The n8n swap (01). Same answers, far cheaper, and it imports as-is.
- The DESIGN.md linter (02). One noul per rule, on every agent's diff. Found 4 violations.
- Component hints (05). Feed the top three picks into the coding agent's prompt.