tests/gateway/test_dm_topics.py::test_setup_dm_topics_loads_persisted_thread_ids tests/gateway/test_dm_topics.py::test_setup_dm_topics_creates_when_no_thread_id tests/gateway/test_dm_topics.py::test_setup_dm_topics_mixed_persisted_and_new tests/gateway/test_dm_topics.py::test_setup_dm_topics_skips_empty_config tests/gateway/test_dm_topics.py::test_setup_dm_topics_no_config tests/gateway/test_dm_topics.py::test_create_dm_topic_handles_duplicate_error tests/gateway/test_dm_topics.py::test_create_dm_topic_handles_generic_error tests/gateway/test_dm_topics.py::test_create_dm_topic_returns_none_without_bot tests/gateway/test_dm_topics.py::test_ensure_dm_topic_creates_on_demand_and_persists tests/gateway/test_dm_topics.py::test_ensure_dm_topic_force_create_replaces_persisted_thread_id tests/gateway/test_dm_topics.py::test_persist_dm_topic_thread_id_writes_config tests/gateway/test_dm_topics.py::test_persist_dm_topic_thread_id_skips_if_already_set tests/gateway/test_dm_topics.py::test_persist_dm_topic_thread_id_replaces_existing_when_requested tests/gateway/test_dm_topics.py::test_persist_dm_topic_thread_id_preserves_config_on_write_failure tests/gateway/test_dm_topics.py::test_get_dm_topic_info_finds_cached_topic tests/gateway/test_dm_topics.py::test_get_dm_topic_info_returns_none_for_unknown tests/gateway/test_dm_topics.py::test_get_dm_topic_info_returns_none_without_config tests/gateway/test_dm_topics.py::test_get_dm_topic_info_returns_none_for_none_thread tests/gateway/test_dm_topics.py::test_get_dm_topic_info_hot_reloads_from_config tests/gateway/test_dm_topics.py::test_cache_dm_topic_from_message tests/gateway/test_dm_topics.py::test_cache_dm_topic_from_message_no_overwrite tests/gateway/test_dm_topics.py::test_build_message_event_sets_auto_skill tests/gateway/test_dm_topics.py::test_build_message_event_no_auto_skill_without_binding tests/gateway/test_dm_topics.py::test_build_message_event_no_auto_skill_without_thread tests/gateway/test_dm_topics.py::test_build_message_event_filters_non_topic_dm_thread_id tests/gateway/test_dm_topics.py::test_build_message_event_preserves_true_dm_topic_thread_id tests/gateway/test_dm_topics.py::test_group_topic_skill_binding tests/gateway/test_dm_topics.py::test_group_topic_skill_binding_second_topic tests/gateway/test_dm_topics.py::test_group_topic_no_skill_binding tests/gateway/test_dm_topics.py::test_group_topic_unmapped_thread_id tests/gateway/test_dm_topics.py::test_group_topic_unmapped_chat_id tests/gateway/test_dm_topics.py::test_group_topic_no_config tests/gateway/test_dm_topics.py::test_group_topic_chat_id_int_string_coercion tests/gateway/test_dm_topics.py::test_build_message_event_dm_from_user_none_falls_back_to_chat_id tests/gateway/test_dm_topics.py::test_build_message_event_group_from_user_none_stays_none tests/gateway/test_dm_topics.py::test_build_message_event_dm_from_user_present_uses_user