The problem
You are building a browser agent (a program that drives a web page on its own). Give it a goal and it clicks its way there. In the first version you let a model write a CSS selector — the little bit of code that points at one element on a page — to say where to click.
It came back with '#login > button.primary'. That id does not exist on this page. On the next page the class it wrote matches three buttons at once. On the page after that the selector has a syntax error in it. Three different ways of being wrong, and not one of them can be caught before you click.
So you try to add a check, and find there is nothing to check against. The set of strings a model can write is infinite; you cannot list in advance which of them are right. The error space has no edges.
The solution
The Menu pattern says: let code list what is on this page and number it, and let the model answer only which number.
Forty visible elements make forty options, each with a short label — the words on the button. The answer space drops from infinite to forty, and '12' is verified in one line: is it between 0 and 39?
Always keep one extra item on the menu that means none of these. When nothing fits, the model should be able to say so instead of picking the least bad of forty wrong things. And if the list was cut — the page had 300 elements and you sent 240 — say so in the material you send, because what the model cannot see does not exist.
Ask · Answer · Act
- AskPut in the page text and forty numbered elements, and ask which one to click next.
- AnswerNumber 12 at 0.71, number 7 at 0.12, none-of-these at 0.03.
- ActNumber 12 is the highest and really is one of the forty, so the code clicks it.
Where the name comes from
A menu. In a restaurant you order what is printed on it; the kitchen will not improvise a dish that is not there. A menu also saves the waiter from guessing which of the spicy ones you meant.
Use it when
Code can produce the candidates, there are fewer than a few hundred of them, and each fits in a short label.
Not when
The right answer only exists once someone writes it — the phrase to type into a search box, the body of a commit message. Or there are more than 255 candidates with no way to split them into chunks.
Projects that do this
- jev-ultrafast — Browser agent where Jev picks each action and element; LLM only types text.
- jev-browser — Fast, cheap browser automation driven by TypeSafe's Jev model.
- jev-review — Staged code-review workflow with a local dashboard, gated by Jev.
Read this code
- jev-ultrafast
jev_ultrafast/model.py:48-78Code numbers every element on the page and groups them by operation; all the model ever sees is numbers, never a selector or a coordinate.
- jev-browser
lib.ts:89-99The clickable things found on the page become names like click_e3 and type_e1; the model can only answer with one of those names.
- jev-review
src/review/judgments.ts:180-191Each block of a change is one option on the menu, with one extra option meaning 'none of these'.
Show me the code
const els = visibleElements(page).slice(0, 40); // code lists them, with a cap
const menu = Object.fromEntries(els.map((e, i) => [String(i), label(e)]));
menu.none = "None of these"; // always leave this way out
const { answers } = await jev(
{ goal, page: pageText, listWasCut: els.length === 40 },
{ target: choice("Which element should be clicked next?", menu) },
);
const pick = answers.target.choice;
if (pick === "none" || !(pick in menu)) return escalate();
await els[Number(pick)].click();How jevbooks recognises it
How do we know a project does this? We put one question about its README to Jev and it returns a percentage; the higher, the more it looks like it. If you copy the question, copy one thing: ask whether the text mentions this one concrete thing, never whether the project is good.
{
"candidate-enumeration": {
"type": "noul",
"instructions": {
"what": "Does the text describe code listing the candidates such as DOM nodes, diff hunks, or files, and Jev choosing an index among them?",
"not_for": "Asking the model to produce a selector, coordinate, or free-text answer."
},
"criteria": {
"true": "The text explicitly describes this. One sentence is enough; other content does not cancel it.",
"false": "The text does not describe this."
}
}
}