What changed, and why
The W3 dispatcher classifies a forwarded receipt and routes it to route_receipt — which was a silent no-op. So a forwarded receipt was accepted by the webhook and dropped on the floor, with the sender left assuming it had been handled.
The receipt⇄transaction join (W4) is a genuine deferred increment: the workflow and its pure planner exist, but the I/O activities are still stubs and there's no email→receipt parser yet. So the agent can't process the receipt — but it shouldn't vanish either. The fix is the honest middle ground: acknowledge it, and point the owner at the path that works today.
Why W4 isn't started: its activities still raise NotImplementedError — starting the workflow would crash and page.
src/ynab_agent/workflow/receipt_activities.py · 47 lines
⋯ 19 lines hidden (lines 1–19)
⋯ 23 lines hidden (lines 25–47)
Acknowledge, don't swallow
route_receipt now replies on the receipt's own thread with an honest note, addressed to the owners, idempotent on the message id. It deliberately does not start ReceiptJoinWorkflow (that would crash on the stubs); it just makes sure the forward isn't silently lost.
A reply instead of a no-op; no-op only when there's no thread to reply on.
src/ynab_agent/workflow/dispatch_activities.py · 250 lines
⋯ 169 lines hidden (lines 1–169)
⋯ 53 lines hidden (lines 198–250)
The honest note: can't match receipts yet → reply on the transaction's thread.