intent dispatch that decides which door a request walks through, fails closed on a request for a door that does not exist, and wires the escalation handoff — a conversation suggestion that becomes a command without a second write path.The stakes. Mira has exactly one invoke seam and two behaviours behind it. If the seam picks the wrong behaviour — or silently invents a default when the caller sends garbage — a conversation could run the frame-rewriting panel, or a malformed invoke could start a run it can never finish. The router is small, but it is the one place that decides "talk vs do", so it has to be total: every input maps to a wired engine or to a typed failure, never to a guess.
intent would quietly get a chat turn instead of a loud error, and a typo like "comand" would do the same. Mira has no default route — an unknown or missing intent raises before any frame is emitted, exactly like a malformed input. Fail closed, always.
Stories B and C each landed their own intent branch in runtime.handle_request, so the dispatch worked before this Story opened. Story-D's job is not to rebuild it but to formalise it: retire the stale "scaffold emits not_implemented" docstrings that no longer describe the code, and pin the two guarantees the caller relies on as tests.
| Guarantee | Where enforced | Test |
|---|---|---|
| Each intent reaches its wired engine, in the frozen event order | runtime.handle_request → _route → converse / command | test_converse_dispatches… / test_command_route_is_wired… |
| Unknown / missing intent fails closed, before any frame | runtime._route (no default; raises ValueError) | test_unknown_intent_fails_closed / test_missing_intent_fails_closed |
_route, not in each caller: the fail-closed check is in the one function both the brain (handle_request) and the watchdog (stream_invoke) resolve the intent through. A guard per branch would be a guard to forget; a guard at the single resolution point cannot be bypassed.RFD §2.3 describes "ok, do it": a conversation turn suggests a mutation, counsel clicks a chip, and it runs as a command. The temptation is to build a third path for that — a suggestion executor. We didn't. A suggested command is the same command door, pre-filled. The only new code is a pure mapping that proves it:
# models.py — SuggestedCommand.as_command_input def as_command_input(self, *, matter_id, current_frame, ...) -> MiraCommandInput: # scope + text pass through UNCHANGED (text -> command); # the caller supplies the FRESH frame (it may have advanced since the turn). return MiraCommandInput(matter_id=matter_id, command=self.text, scope=self.scope, current_frame=current_frame, ...)
scope + text); the caller binds it to the current frame at fire time, so the command is always built against live truth — the same stale-base discipline base_frame_version exists for (RFD §6).The round-trip test builds a MiraConverseTurn with a suggested_command, fires it through as_command_input into handle_request, and asserts it lands on the command route producing run.started → model.usage → turn.completed → run.completed with command == {scope:"issues", text:"make the issues bullet points"} and requires_counsel_approval == true. Same door counsel uses directly — no second write path.
The suggested_command field exists on the wire, but in v1 the model does not emit it — the conversation turn ships it as None and the always-available UI "Command" affordance (RFD §2.3 mechanism 1) is the escalation. The typed shape is present so the app and the wire are ready; the upgrade is a cheap second non-streaming classify call after the stream closes (RFD §2.3 option b), which keeps conversation pure-text-streaming and Bug-1370-safe.
| File | Change |
|---|---|
| models.py | SuggestedCommand.as_command_input(...) — the escalation handoff as a pure, tested mapping; docstring pins the v1-lazy contract + the option-b upgrade path. |
| runtime.py | De-staled the scaffold docstrings to describe the wired dispatch this Story formalises (no behaviour change — dispatch was live from B/C). |
| converse.py | One comment at the turn build site: suggested_command stays None in v1 (lazy), upgrade = option b. |
| tests/test_runtime.py, tests/test_models.py | Round-trip handoff test + the as_command_input unit test; refreshed the stale scaffold docstring. |
uv run --package mira pytest → 52 passed. ruff check agents/mira/ → All checks passed.