T 169
uid: T-169 title: Telegram bot β substantiation-note prompt must identify WHICH receipt it's asking about status: done area: telegram-receipts created: 2026-07-16 updated: 2026-07-16 owner: girafeev1 assignee: Records (Infrastructure) related: T-067, T-115
Goal (owner, 2026-07-16)¶
When a receipt is uploaded without a caption, the bot's follow-up ask read only
"π Send a business-purpose note for this receipt β¦ note:7412172786-β¦-9714ffa8" β with several
receipts in flight the user cannot tell WHICH receipt the bot means; the note:<id> token is
machine-correlation, not human information. Owner: adjust the process so the follow-up carries
enough for the user to differentiate.
Why it's built this way (constraint)¶
The prompt fires immediately on photo receipt β before the AI parse runs (the note is captured
pre-Inngest per DIPN 21), so merchant/amount don't exist yet at prompt time. That's why the
message was bare. The differentiators that DO exist at that moment: the photo message itself and
the upload time. So: quote the photo (Telegram reply_parameters β the thumbnail renders right
above the ask) + stamp the upload time into the text. No pipeline reordering, no schema migration;
the note:<pendingId> token and the force-reply matcher are untouched.
What shipped (pages/api/telegram/receipt-webhook.ts)¶
- Pending doc gains
photoMessageId(the original photo's Telegram message id) so every later prompt about that receipt can quote it β including the note-edit re-prompt and the defensive re-ask, which previously had no handle on the photo. - Shared
notePromptText(pendingId, sentAt, quoted): "β¦for the receipt quoted above (photo sent Jul 16, 14:02) β¦" β HK-time viaformatHkTime;toDateMaybecoerces the Firestore Timestamp. Legacy pendings withoutphotoMessageIddegrade to the time-only wording; nothing breaks mid-flight. tgSendForceReply+tgSendMessageWithKeyboardgain optionalreplyToMessageIdβreply_parameters: { message_id, allow_sending_without_reply: true }(deliverable even if the user deleted the photo). All 3 note-prompt sites + the caption-confirm prompt now quote the photo.
Log¶
- 2026-07-16 β created + done (Records Infrastructure; owner request in-session; owner chose to keep this with Records rather than spawn a dedicated Telegram-bot agent β the bot is in-repo Records plumbing and the change is contained). β Read AGENTS.md Β· checked the board by scope (T-007 dropped / T-067 / T-115 adjacent, none cover the note-prompt UX; no dup). Source: Records (Infrastructure) Β· https://claude.ai/code/session_018RDB37kCqfouHdygVXTAtD
- Verdict: shipped as scoped β quote-the-photo + upload-time differentiation on every
substantiation-note prompt; correlation token unchanged. Full unfiltered
tsc --noEmitclean (after clearing stale.next/typesfrom the bank-accounts retirement β not from this diff). - Merged to
main, no deploy (owner: commit + merge every change; deploys manual β a deploy was cancelled mid-build by the owner earlier today to batch more work; this rides the next one). - Blast-radius:
pages/api/telegram/receipt-webhook.tsonly (pending-doc shape gains one nullable field written at create; readers tolerate its absence). The Inngest processor, receipt store, and web Records page are untouched. Owner runtime-verify: upload 2+ receipts without captions in quick succession β each prompt quotes its own photo thumbnail + shows its upload time; reply to one prompt β the note lands on the right receipt (token matching unchanged).