Day 288 of DailyBuild. Today's build is Photo Gate: one photo in, five typed decisions out, in a single forward pass, on my laptop, with no API key and no tokens generated.

I gave it a job I actually care about. Would this photo get a click on a second hand marketplace, or does the seller need to go take another one?

What a decision model is

You send a state, which here is an image plus a short line of context. You send questions with lettered options. The model reads the option letter logits at each answer slot in one forward pass and returns a probability for every option. It does not write a sentence. It cannot invent an answer outside the options you gave it.

That is the whole idea of the class. Jev and the twenty alternatives that shipped after it are all built on it. TypeSafe's whole pitch is that most useful model calls are decisions, not conversation, and decisions should cost one pass.

I have run decision models on chess and on racing games before. This one was different, because the model I picked can see. Mapika/decider-2b-vision is a 2B Qwen3.5 vision language model with the decider text weights transplanted in. Apache 2.0, 4.4 GB, and it runs on an 8 GB M2 mini at about six seconds per photo.

The part that makes the numbers checkable

A demo that shows a green badge proves nothing. So I built a corpus with known answers.

22 real CC0 photographs of second hand saleable things, chairs, lamps, cameras, bikes, guitars, jackets. Then I damaged each one by a measured amount: motion blur at a stated kernel length, defocus at a stated sigma, exposure in stops, crops that keep a stated fraction of the frame, downsampling to a stated fraction before upscaling, JPEG quality set to a number.

484 variants. Each one has a label the builder applied before the model ran: mild damage is still usable, moderate is retake, severe is unusable.

That label is a spec, not a photographer. I say so in the repo and I say it here, because the difference matters and I am not going to dress a rule up as human annotation.

The result I did not expect

45.2% accuracy. That is bad, and it is not the interesting part.

The interesting part is this: out of 484 decisions, the model said retake twice.

Twice. On a three way question, it used two answers. Every mild problem got called usable, every moderate problem got called unusable, and the middle option that the whole severity scale is built around never got picked.

It also thinks clean photos are suspect. The 22 undamaged photographs got a mean P(usable) of 0.38.

But it knows exactly what is wrong

Here is where it gets good. I also asked it four narrower questions: is it sharp, is it exposed correctly, is the whole item in frame, how would you rate the quality.

The damage only has one true answer for those questions, so I could score them separately. Focus: 88% right. Exposure: 82% right. Underexposure, 91%. Low resolution, 97%. Defocus, 82%. Motion blur, 80%.

And the probability curve behaves. Mean P(usable) falls from 0.37 to 0.08 as motion blur goes from mild to severe. Defocus 0.38 to 0.06. Overexposure 0.37 to 0.07. Low resolution 0.40 to 0.08. Too dark is the weakest of the five at 0.37 to 0.19, and it is the only one that stays ambiguous, which fits: a dark photo of a dark object is genuinely hard to call.

So the model can see. It can tell me the photo is blurry with 88% accuracy. What it cannot do is map that perception onto a three way accept/retake/reject label with sane thresholds.

Two things it genuinely cannot see. Cropping: P(usable) goes slightly up as the frame closes in, because a tidy cropped photo looks tidier. JPEG damage: invisible at 448 pixels, and P(usable) rises slightly with compression because compression smooths noise.

The blind pass is the test I would run on any vision model now

Every item got asked twice. Once with the photo, once with the photo withheld.

If the answers do not move, the model was reading the prompt and the image was decoration. Vision lift on the verdict: 45.2% with the photo against 36.4% without, so 8.9 points. Only 33% of verdicts were identical between the two passes, and P(usable) moved by 0.19 on average.

The photo is doing real work. That is the number I would want to see before trusting any vision claim, and it is the number that almost nobody publishes.

What I would change if I shipped this

Ask two questions, not one. Is this photo acceptable? as a yes or no, then how bad is it? as a scale. A three way choice that a 2B model collapses to two answers is a badly shaped question, and the collapse tells you that before any threshold tuning.

Keep the four narrow checks. They are the useful output. The verdict is the weakest thing the model produces here and it is the thing I put in the biggest font, which is a lesson about demo design.

What this does not prove

One model, one size, 22 base photographs, one machine. The labels are a rule I wrote. The post temperature calibration number is fitted on the same rows, so it is the best a single scalar could do, not what a new photo would get. Batch size matters more than it should on 8 GB: four images per pass costs about six seconds each, six images pushes the machine into swap and costs twenty five.

This is a demonstration of what a decision model does, not a ranking of decision models.

The build

484 decisions, 0 tokens generated 6.4 s per photo on an 8 GB M2 mini, one forward pass 45.2% verdict accuracy, 88% on "is it sharp" +8.9 points of accuracy from having the photo at all 

Everything runs on 127.0.0.1. No API key, no cloud, no accounts. The checkpoint downloads once, after you press the consent button, and says so.

Screenshots

Photo Gate 1

Photo Gate 2

Photo Gate 3

Photo Gate 4

Photo Gate 5

Code & more: https://www.dailybuild.xyz/project/288-photo-gate