Skip to content
Franklin Martinez
All work

justask

Plain-language requests into an app's own state, with measured gates

Abstract. justask turns what a person types into an app's own state: a search result, a table filter, a filled record card. Code finds the candidates, a model picks one label per question with a probability for every label, and a field the model is unsure of stays empty for the person to fill.

New expense

“lunch with Larkspur yesterday, $86.40”

Vendor

Larkspur Catering

0.99 gate 0.70
Tags

Meals

0.86 gate 0.40
Date

Sat, Oct 10, 2026 “yesterday”

0.98 gate 0.80
Amount

$86.40 “$86.40”

1.00 gate 0.90

New expense

“lunch with the cleaners yesterday, $40”

Vendor

Left empty for you

0.17 gate 0.70

No vendor is named. Best vendor guess: Larkspur Catering at 0.17.

Tags

Meals

0.95 gate 0.40
Date

Sat, Oct 10, 2026 “yesterday”

0.98 gate 0.80
Amount

$40.00 “$40”

1.00 gate 0.90

New expense

“Farwander taxi with a client on Friday, $64”

Vendor

Farwander Travel

0.81 gate 0.70
Tags

Travel

0.70 gate 0.40

Client

0.86 gate 0.40
Date

Fri, Oct 9, 2026 “Friday”

0.99 gate 0.80
Amount

$64.00 “$64”

1.00 gate 0.90

Pick a request

Try it live
Fig. 1. Three requests sent to the live demo on October 11, 2026, replayed with their real responses. Bars are the model's probability for each pick; a pick below its gate leaves the field empty.

The problem

Natural-language input in business apps usually means a model writes free text and the app hopes it parses. A model that writes can invent a vendor that doesn’t exist, a date nobody said, or an amount in the wrong currency, and the app saves it anyway.

The design

justask never lets the model write. Code does the reading it can do reliably: it parses dates, times and amounts, and shortlists candidate rows from the host app’s own catalog. The model then answers one question per field by picking one label from that list, and returns a probability for every label. Code builds the result from the picks.

Each field has a gate. A pick below its gate is held, so the field stays empty and the person fills it. Nothing reaches the host app until the person confirms. A held field is not a failure: it is the system saying it does not know.

Measured, not guessed

Gates have no default value. The library makes the host app declare each one, because a threshold that ships as a default is a guess somebody else made. I measure mine on frozen eval sets: each set is fixed by checksum, with its pass lines written down, before the first model call. Round 4 of the search eval picked every request exact, invented none and held every ambiguous one. The failed rounds before it are in the eval log.

The limits are written down too. The pre-launch QA found a small share of fills confident and wrong on a broad probe set, which is why the confirm step is not optional. A check for obvious prompt-injection markers holds every field when it fires, and the README says plainly that it is a narrow check, not a defence.

The live demo

The demo is a fictional invoicing app with a table, a form and a search case, in English and Spanish. It runs on one Cloudflare Worker, rate limited and capped on model spend. Fig. 1 above replays three real responses recorded from it.

What ships

@justask/core on npm under MIT: the core, a React layer of hooks and unstyled pieces, the provider adapter, and the eval functions used to measure gates. It also ships an agent skill that teaches coding agents what they get wrong with it: the model picks and never writes, gates have no default, and a held field is no failure.

Talk about this project