Outcome: an operator can distinguish a healthy Vani answer, a deferred broker round trip, and a rejected empty turn — without inspecting matter content.
model call closes → tool phase classifies answer / deferred / unavailable → Vani assembles the final turn → terminal or deferred producer span opens → turn.completed enters the broker stream
WARNING mira_converse_empty_terminal_blocked run_id=… reason=empty_answer
and then raises VaniEmptyTerminalTurnError, so the run fails instead of
emitting a turn. This Story does not add a second warning for the same
condition. Consequence for the spans: an empty terminal turn is charged and recorded
(agents.mira.tool_phase opens first, deliberately), but never reaches
agents.mira.terminal_turn — that span means a turn actually handed over.
No log or span here carries prompt, matter text, or the generated answer.| Registry name | Why it exists | Kind |
|---|---|---|
agents.mira.tool_phase |
Records the one decision between an answer, deferred intent, and unavailable tool result. | INTERNAL |
agents.mira.deferred_intent_dispatch |
Marks the producer boundary where any deferred intent leaves Vani for broker execution, including terminal Vega delegation. | PRODUCER |
agents.mira.terminal_turn |
Marks the final user-visible turn actually handed to the broker (a rejected empty turn never reaches it). | PRODUCER |
manual_span(...) validates one closed-registry name and one allowlisted
attribute map, then opens the same span on global ADOT for CloudWatch/X-Ray and on Vani's
dedicated Logfire provider when that lane exists. Without LOGFIRE_TOKEN, the
mirror is a no-op and the turn continues.
The spans carry run_id, agent.key=mira,
gen_ai.agent.name=Vani, model_id, seat,
markdown_len, intent_count, is_terminal,
tool_called, unavailable_returned, and
channel_origin. They never carry firm_id, prompt text, matter
identifiers, matter content, or markdown.
| Requirement | Test |
|---|---|
| A terminal turn opens exactly tool_phase then terminal_turn, content-free | test_terminal_turn_opens_the_two_registry_spans |
| The two spans are peers under the inbound parent, not nested | test_dual_spans_are_peers_under_the_same_inbound_parent |
firm_id is stamped on the agent-run root only, never the children |
test_firm_id_is_stamped_only_on_the_agent_run_root |
| ADOT and Logfire receive identical span data | test_manual_span_dual_emits_the_same_name_kind_and_attributes |
| Missing or broken Logfire cannot break the turn | test_absent_logfire_lane_is_a_noop and test_broken_logfire_lane_cannot_break_the_operation |
| Deferred broker boundary is emitted | test_fetch_ask_emits_a_non_terminal_resolve_intent_turn |
| Prompt/matter attributes are rejected | test_prompt_and_matter_content_attributes_are_rejected |