T 172
uid: T-172 title: Receipt pipeline fits Hobby windows β split the Inngest mega-step (fixes I-036) status: done area: telegram-receipts created: 2026-07-21 updated: 2026-07-21 owner: girafeev1 assignee: Records (Infrastructure) related: T-170, T-171, I-036
Goal (owner, 2026-07-21: "make them actually work")¶
Receipt uploads died with Inngest's "Your server returned HTTP 504 before the SDK responded"
(I-036): the pipeline's single process-receipt step ran extract + Gemini clean(Γ3) + verify +
crop in ONE /api/inngest invocation β "~10s of pipeline" on Pro that exceeds the 60s window on
Hobby compute (Vercel SIGKILL β 504 β Inngest retry loop).
What shipped (lib/inngest/receipt-processor.ts, lib/telegram/receiptProcessor.ts)¶
- The mega-step is split so each serverless invocation holds ~one Gemini round-trip:
parse-fieldsβclean-image-1..3(ONE clean+verify attempt per step; drift retries get fresh invocations) βcrop-fallback(deterministic crop + sharp polish, only when no verified clean). Phase order, retry semantics, drift handling, fallbacks, and the assembledprocessedobject are byte-identical toGeminiReceiptProcessor.process(); state rides between steps as JSON/base64 exactly like the existing download step. - DocumentAI escape hatch preserved:
extractFieldsfailure runs the processor's fullprocess()(which carries the DocumentAI fallback) in one step β the old behavior for that path. - Interface:
ReceiptProcessorgains OPTIONALextractFields/cleanImage/verifyCleanedImage; the split engages only when all three exist β a processor without them (e.g. DocumentAI-primary) keeps the original singleprocess-receiptstep. - Works hand-in-hand with T-171 phase 2:
RECEIPT_CROPPER_URLnow points at the NAS cropper, so thecrop-fallbackstep's crop is a fast remote call instead of skipped-entirely (the prod URL slot previously held a 1,124-char pasted JSON blob β same accident family as the deleted dev CRON_SECRET β so NO cropping ran anywhere; documented in T-171).
Log¶
- 2026-07-21 β created + done (Records Infrastructure; owner "Let's move everything to the NAS and make them actually work"). β Read AGENTS.md Β· checked the board by scope (I-036 is the symptom ticket; T-171 owns the infra side; this task owns the code split β no dup). Source: Records (Infrastructure) Β· https://claude.ai/code/session_018RDB37kCqfouHdygVXTAtD
- Full unfiltered
tsc --noEmitclean. Merged tomain; activation deploy per T-171 phase 2 (one deploy carries the split + the NAS service env). Owner runtime-verify: upload a captionless receipt β note prompt quotes the photo (T-169) β processing completes without the 504; status line walks Reading β Cleaning β (Verifying/Cropping) β summary. - Blast-radius: the Inngest receipt function's step layout changed (in-flight runs at deploy
time may replay oddly β one-off);
ReceiptProcessorinterface widened (optional members only). Webhook, store, Records page untouched.