intake: the word "Image" is not a receipt, and the prompt must not be data
ci / lint-test (push) Successful in 52s
ci / lint-test (push) Successful in 52s
Batch 3d2ffbe8 arrived as one text part whose entire content was the five characters `Image` — an iOS Shortcut coerced the shared photo to text. The model was asked anyway and answered out of its prompt: Burgertory (Braybrook), two "The Pride" lines at $22.80, a $41.15 total. Nothing on it existed. Burgertory was in ORDER_ITEM_PROMPT as the example of how to print a branch in parentheses. Only the missing date kept it out of the matcher; the invented header and items were stored on the batch regardless. Three layers, none of them a model: 1. textCouldBeReceipt() — deterministic, before any model call. A TEXT part with fewer than two words, no digit, or under 40 characters and no money figure cannot be a receipt. Length is NOT the test on its own: `TOTAL $17.00` is twelve characters and is exactly what this door accepts. A rejected part is dropped like an unreadable file type, so one junk part beside a good screenshot still goes through; if nothing usable is left the card quotes what the phone actually sent. PDFs and images untouched. 2. The prompt names no real shop — merchant examples are PROMPT_PLACEHOLDER_MERCHANTS, exported from intake-rules so the prompt and the guard cannot drift — and it is told what to return when the input is not a receipt at all. 3. confabulationReason() — the belt. Reachable where the pre-check cannot look: a PDF that renders to a scrap of junk still reaches the model. Also rejects any answer whose merchant IS a placeholder. All three file `unsupported`, never `needs_header`. A confabulated header is not a partial one, and the needs_header path stores what it is given. Same shape as the ingestion engine's 2026-08-05 empty-body defect: a stage that can be handed nothing has to say so, because the model downstream will always answer. Tests: 16 unit cases over the two pure functions and the prompt; three integration cases — `Image` is declined with zero model calls and nothing but `unsupported` on the batch row, a junk part beside a real receipt still enriches, and a junk PDF is unsupported rather than needs_header. The first and third fail on the tree before this commit.
This commit is contained in:
@@ -699,6 +699,25 @@ and the cross-repo contract are in smarthome `plans/intake-router-slice-plan.md`
|
||||
delivery order's total is items + fees − promotions ($118.94 of Coles lines
|
||||
became $85.74), so checking lines against the total would hold every
|
||||
delivery order. Tolerance $1 or 2%; a failure is `held`, nothing written.
|
||||
- **Nothing that cannot be a receipt reaches the model, and the prompt names no
|
||||
real shop.** On 2026-08-22 an iOS Shortcut coerced a shared photo to text, so
|
||||
a batch arrived as the five characters `Image` — and the model returned a
|
||||
whole receipt: `Burgertory (Braybrook)`, two "The Pride" lines, $41.15. None
|
||||
of it existed; "Burgertory" was in `ORDER_ITEM_PROMPT` as the example of how
|
||||
to print a branch in parentheses. Only the absent date kept it out of the
|
||||
matcher, and the invented header and items were still stored on the batch as
|
||||
`needs_header`. Three layers now, in order: `textCouldBeReceipt()` rejects a
|
||||
TEXT part with fewer than two words, no digit, or under 40 characters with no
|
||||
money figure (`TOTAL $17.00` passes — length alone is *not* the test)
|
||||
**before** any model call; the prompt's merchant examples are
|
||||
`PROMPT_PLACEHOLDER_MERCHANTS` format placeholders and it is told to return
|
||||
nulls for a non-receipt; and `confabulationReason()` catches what the
|
||||
pre-check cannot see — a PDF that renders to junk — plus any answer echoing a
|
||||
placeholder. All three file `unsupported`, never `needs_header`: a
|
||||
confabulated header is not a partial one. Same shape as the ingestion
|
||||
engine's 2026-08-05 empty-body defect — **a stage that can be handed nothing
|
||||
must say so, because the model downstream always answers, and it answers out
|
||||
of its prompt.**
|
||||
- **Names stay verbatim.** Burgertory calls its burgers "Michael" and "Joel";
|
||||
the prompt forbids canonicalising and the stored shape is the order lane's
|
||||
(`qty`, `description`, `amount`, `options`) so `order-details.tsx` renders it.
|
||||
|
||||
Reference in New Issue
Block a user