fix(customer): never send an unsubstituted placeholder or a fake customer id

With DISPATCH_AGENT autonomous=true in the agent registry, the assignment-failure
path sends a real customer message. It would have sent
  "Delay alert: Order 36 may arrive later than expected. New ETA: {new_eta}"
with customer_id "unknown": the DELAYED template names {new_eta} but DispatchAgent
supplied only order_id, and it has no customer id to give.

- _send_via_channel refuses to send when any {placeholder} survives substitution
  (or the template is missing), logs which variables the caller omitted, and
  records a failed notification. Silence beats a broken message.
- customer_id is omitted from the payload when it is absent or "unknown" rather
  than sent literally; the backend resolves the recipient from order_id.
- DispatchAgent supplies new_eta "being confirmed" — honest, since no ETA exists
  at assignment-failure time.
- Tests: the old bug is refused, a complete payload renders clean, the id is
  omitted/passed correctly, and DispatchAgent's vars satisfy the template.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012AJLYcbTHCe45fyFnMfEin
This commit is contained in:
2026-09-30 15:14:08 +05:30
parent 5a7d32cc04
commit 9cbb6f9704
3 changed files with 193 additions and 15 deletions

View File

@@ -474,11 +474,15 @@ class DispatchAgent(SpecializedAgent):
recipient="CUSTOMER_AGENT",
message_type=MessageType.AGENT_TASK,
payload={
# CUSTOMER_AGENT's real task contract (send_notification + DELAYED template)
# CUSTOMER_AGENT's real task contract (send_notification + DELAYED
# template). Every variable the template names must be supplied or
# the send is refused; we have no ETA at assignment-failure time,
# so say so rather than inventing one. No customer_id: the event
# carries none, and the backend resolves the recipient from order_id.
"task_type": "send_notification",
"order_id": booking_id,
"notification_type": "delayed",
"template_vars": {"order_id": booking_id, "reason": "finding the right rider"},
"template_vars": {"order_id": booking_id, "new_eta": "being confirmed"},
},
correlation_id=str(booking_id),
)