Combine malformed tool-call handling with placeholder filtering and a no-tools fallback so a relay that returns tool_use blocks with null id/name/input can no longer crash a turn or permanently wedge a session. Adapted to the ContextGovernor architecture (context governance now lives in nanobot/agent/context_governance.py, not runner.py): - ToolCallRequest.has_valid_name(): single source of truth for "usable name" (non-empty string). - tool_hints.format_tool_hints(): skip tool calls with a non-string/empty name instead of raising AttributeError on the whole turn. - ContextGovernor.strip_placeholder_assistant_messages() and strip_malformed_tool_calls() (plus the _tool_call_name_is_valid helper): history-cleaning staticmethods invoked at the START of prepare_for_model() — strip_placeholder, then strip_malformed, then the existing drop_orphan/backfill chain. Both only repair the model-facing copy and leave persisted history untouched (return a copy, or the same list when nothing changes). Also wired into runner's minimal-repair path. - AgentRunner._drop_malformed_tool_calls(): returns (dropped, all_dropped, original_finish_reason); clears finish_reason to "stop" when all calls are dropped. - AgentRunner._malformed_tool_call_retry_messages() + _request_model malformed_retry flag: when an all-dropped tool_calls response comes back, retry once with a corrective note; if the retry STILL comes back all-dropped, fall back to _request_no_tools for graceful text degradation. Tests for the history-cleaning methods live with ContextGovernor in tests/agent/test_runner_governance.py; response-layer and tool-hint tests stay on AgentRunner / tool_hints. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>