Twenty minutes a listing
My client resells used electronics parts. Every item that comes through the door has to become a listing, and every listing starts the same way: pick the thing up, turn it over, read the label, work out what it actually is, then go and find out what it's worth.
He put it at about twenty minutes an item. Most of that isn't judgment. It's squinting at a worn sticker, typing a model number into a search box, opening six tabs of completed listings, and forming an average in his head. The judgment — is this worth buying at that price, is this one worth refurbishing — happens after all of it, and takes about ninety seconds.
So the twenty minutes wasn't the job. It was the tax on getting to the job.
Two hard problems wearing one coat
The obvious framing is "AI writes my listings." That framing is why most tools in this space are bad.
Identifying a part and pricing a part are different problems with different failure modes. Identification is a reading problem — the answer is physically printed on the item, and the work is transcription under bad conditions: glare, wear, a photo taken at an angle in a garage. Pricing is a research problem — the answer isn't on the item at all, it lives in what similar items recently sold for.
Conflating them produces the worst possible failure: a confident price attached to the wrong product. And that error is invisible, because a plausible number next to a plausible product name looks exactly like a correct one.
So the pipeline keeps them apart, and makes the second depend explicitly on the first.

Reading the label
The identify step runs on the stronger vision model and returns a fixed shape: brand, model code, product type, a verbatim transcription of every readable character on the label or box, a list of visible defects, a packaging-mismatch flag, and its own confidence.
The verbatim transcription is the part that matters most, and it exists because of a specific failure. Ask a model "what is this?" and it will summarise — it tells you what it concluded. Ask it to transcribe first, and you get the evidence it concluded from. When the identification is wrong, the transcription is usually right there showing you why.
The packaging-mismatch flag comes from the same instinct. Used parts arrive in whatever box was handy, and a box is a very confident-looking piece of evidence for the wrong answer.

Two models, one decision
Text generation — titles, captions — runs on the fast model. Identification and pricing run on the stronger one.
That split isn't a preference, it's a cost decision made once and written down. Titles are cheap to get slightly wrong; you read the title. Misreading a model number is expensive, because it silently poisons the price that depends on it, and nobody re-reads a number that looks fine.
Paying more per call on two of four operations, and less on the other two, is the whole optimisation. It took one comment in the source to record why, and that comment is why the decision survives the next person to touch the file.
When it doesn't know
Once the part is identified, two searches run in parallel: one for manufacturer specs, one for local price comps.
If both come back empty, that isn't treated as a search problem.
Two independent queries finding nothing is not an absence of evidence. It's the signature of a misread.
A real part with a real model number has something written about it somewhere. When both searches return nothing, by far the likeliest explanation is that the brand or model coming out of the identify step is wrong — which means anything built on top of it is wrong too.
So the empty result is passed forward as an explicit instruction: the brand and model may be misread, keep confidence low, do not invent specs. And the estimate comes back carrying a flag the seller can't miss — "Brand/model unverified — no search results found" — merged into the same "check before posting" list as visible defects and packaging mismatches.
I want to be precise about what this does and doesn't do, because the tempting version of this story is that the system refuses to answer, and it doesn't. It still produces a number. What it refuses to do is produce that number silently.
That distinction is the whole design. A hard refusal sounds more rigorous, but it hands the seller nothing and gets routed around within a week — people don't stop pricing items, they just stop using the tool for the hard ones, which are exactly the ones worth helping with. The failure actually worth preventing is narrower: a price that looks identical to a good price. ₱2,400 in the right field, in the right font, with nothing to catch. Flagging costs the seller five seconds and removes precisely that.
Uncertainty doesn't have to block the answer. It has to travel with it.

The cost cap that fails closed
Every AI action checks a per-user rate limit — per minute and per day — enforced in Postgres against the authenticated user rather than in application memory.
The limiter denies on error. If the check itself fails, the answer is no.
That's backwards from how most guards are written, and deliberately so. This limiter is the only thing standing between a UI bug and an unbounded bill on a metered API. A limiter that opens when the database hiccups isn't a limiter — it's a limiter-shaped object that stops working precisely when something is already going wrong. The failure it prevents is financial and silent, which is the combination worth being strict about.

What it demonstrates
The reseller still writes the listing. He edits the title, he overrides the price when he knows something the comps don't, and he's the one who posts it. What changed is that he starts from a filled-in form and a defended number instead of a blank one and six open tabs.
The shape underneath is the transferable part, and it isn't about parts at all:
Identify the thing from messy real-world input, and keep the evidence separate from the conclusion. Enrich it from live external sources rather than from what a model remembers. Ground the expensive judgment in what the enrichment actually returned. Decline when the inputs don't support an answer. Hand a person a decision with its reasoning attached, and let them be the one who acts.
Swap the part for a lead and that is lead enrichment — same pipeline, same guards, same reason the human stays at the end of it. Swap it for an inbound ticket and it's triage. The domain moves. The architecture doesn't.

