Skip to main content
Use this when support, QA, or partners need to understand why a user went to one specialist instead of another, or why a route silently fell through to fallback.

Concept

Every HANDOFF decision, whether deterministic (WHEN: intent.category == "billing") or semantic (WHEN: "the request is about billing"), produces a trace event describing what was evaluated and why a target was or wasn’t selected. The runtime does not just log “routed to Billing_Agent” — it records the condition it evaluated, the context available at evaluation time, the outcome, and (for temporary child agents) what came back through ON_RETURN. One thing surprises people the first time they read a routing trace: not every routing event is visible at every trace verbosity level. The runtime supports minimal, standard, verbose, and debug verbosity. If you’re not seeing an event you expect (like handoff_condition_check for a route that didn’t match), raise verbosity before assuming the event doesn’t exist.

Minimal working example

customer_id, account_id, issue_summary, and conversation_summary are shown as passed context fields; declare them via MEMORY or populate them via GATHER/tool results in your actual project — this example assumes they already exist as project-local session values.

How it works

  • The supervisor keeps ownership until a HANDOFF condition matches, evaluated in authored order.
  • Each evaluated condition emits handoff_condition_check with a shape similar to:
    For a matched deterministic route, the runtime also emits deterministic_routing and deterministic_handoff; for a matched semantic (quoted natural-language) WHEN, whenOutcome reflects the semantic evaluation path instead.
  • If a condition references a variable that is not yet set anywhere in scope, the runtime emits route_condition_unresolved instead of silently treating it as a non-match — check for this event before concluding “the condition just didn’t match.”
  • If a flow-level return suppresses a condition that would otherwise have matched (for example, the conversation is mid-flow and routing is intentionally deferred), the runtime emits handoff_condition_suppressed.
  • Once a target is selected, handoff_context_pass_resolved records what the resolved CONTEXT.pass values actually were, and handoff_context_set_applied records any CONTEXT.set writes applied to the child.
  • When EXPECT_RETURN: true is used (as in the fallback route above), the child’s return path emits handoff_return_handler, and ON_RETURN.action: resume_intent produces a resume_intent trace entry showing what intent processing resumed with.
  • Remember: since HISTORY is omitted from every HANDOFF above, each child receives the full conversation history by default (the platform default when HISTORY is unset). Set HISTORY: auto or another explicit strategy if you want bounded/summary history instead, and expect the trace’s history-related fields to reflect whichever strategy is in effect.
  • _meta.* values (like _meta.entry_channel above) are not routing logic — they’re custom trace dimensions for filtering/grouping in dashboards. You can SET _meta.* in ON_START, in flow steps, in lifecycle hooks, and in ON_ERROR/ESCALATE triggers, not only at conversation start.

Common variations

Debugging a semantic (natural-language) condition

Quoted WHEN text like WHEN: "the request does not clearly match billing or technical support" produces the same handoff_condition_check event, but whenOutcome distinguishes the semantic evaluation path from a deterministic one. There’s no separate event name to search for — filter on whenOutcome instead.

Debugging multiple intents in one message

Multi-intent routing emits its own event family instead of a single glob: multi_intent_plan_built (the queue was constructed), multi_intent_target_resolved (a queue entry resolved to a target agent), multi_intent_queued (an entry queued for later processing), multi_intent_sequential plus multi_intent_sequential_task_start/_task_complete/_executed (sequential processing), and multi_intent_parallel (parallel processing). Search for the specific event you expect rather than a wildcard.

Debugging trace visibility

If an event you expect isn’t showing up, check the trace verbosity setting first (minimal/standard/verbose/debug) before assuming the routing logic is wrong — some events only appear at verbose or debug.

Verification

  • Parse and compile the ABL and confirm there are no parser or compiler errors/warnings.
  • Test at least one matching utterance for each deterministic route, one for the semantic fallback, and one that should trigger route_condition_unresolved (reference a variable you know is unset).
  • Inspect the trace for: the selected target, the condition actually evaluated (matches your literal authored text), evaluatedContext, and — for EXPECT_RETURN: true routes — the handoff_return_handler and resume_intent entries.
  • Raise trace verbosity to verbose or debug if an expected event isn’t appearing.

Production readiness checklist

  • Every specialist route has a clear owner and a concise context summary.
  • Deterministic conditions use declared, gathered, tool-result, runtime, or returned fields.
  • Semantic conditions are quoted as natural-language WHEN text.
  • Temporary child agents (EXPECT_RETURN: true) produce every field mapped in ON_RETURN, verified via handoff_return_handler/resume_intent traces.
  • Fallback behavior is explicit and does not hide missing intent coverage — confirm via route_condition_unresolved and handoff_condition_suppressed that nothing is silently falling through.
  • _meta.* dimensions are stable, low-cardinality values (not raw user text), set consistently across the surfaces where routing decisions can occur (not just ON_START).
  • Trace verbosity is set appropriately for the environment (higher in staging/debugging, tuned for volume/cost in production).

Common mistakes

Troubleshooting