@@ -773,12 +773,20 @@ async fn dispatch_message(
773773 . as_ref ( )
774774 . map ( |o| o. lifecycle_reactions )
775775 . unwrap_or ( true ) ;
776- let thread_id = if threading_enabled {
777- message. thread_id . as_deref ( )
776+
777+ // --- Auto-thread: decide intent now, but create AFTER all policy guards ---
778+ let auto_thread_name = if !threading_enabled && message. thread_id . is_none ( ) {
779+ adapter. should_auto_thread ( message) . await
778780 } else {
779781 None
780782 } ;
781783
784+ // thread_id is resolved later, after all guards pass.
785+ // Always propagate an existing thread_id (message arrived inside a thread),
786+ // regardless of threading_enabled — that flag controls explicit threading config,
787+ // not auto-detected thread context.
788+ let mut effective_thread_id: Option < String > = message. thread_id . clone ( ) ;
789+
782790 // --- DM/Group policy check ---
783791 if let Some ( ref ov) = overrides {
784792 if message. is_group {
@@ -839,12 +847,42 @@ async fn dispatch_message(
839847 if let Err ( msg) =
840848 rate_limiter. check ( ct_str, sender_user_id ( message) , ov. rate_limit_per_user )
841849 {
842- send_response ( adapter, & message. sender , msg, thread_id, output_format) . await ;
850+ // Rate-limit rejection: don't create a thread, use existing thread if any
851+ send_response (
852+ adapter,
853+ & message. sender ,
854+ msg,
855+ message. thread_id . as_deref ( ) ,
856+ output_format,
857+ )
858+ . await ;
843859 return ;
844860 }
845861 }
846862 }
847863
864+ // --- Create auto-thread NOW (after all policy guards have passed) ---
865+ if let Some ( ref thread_name) = auto_thread_name {
866+ match adapter
867+ . create_thread ( & message. sender , & message. platform_message_id , thread_name)
868+ . await
869+ {
870+ Ok ( new_thread_id) => {
871+ info ! (
872+ "Created auto-thread {} for message {}" ,
873+ thread_name, message. platform_message_id
874+ ) ;
875+ effective_thread_id = Some ( new_thread_id) ;
876+ }
877+ Err ( e) => {
878+ warn ! ( "Failed to create auto-thread: {}" , e) ;
879+ }
880+ }
881+ }
882+
883+ // Resolve final thread_id reference used by all downstream send_response calls
884+ let thread_id = effective_thread_id. as_deref ( ) ;
885+
848886 // Handle commands first (early return)
849887 if let ChannelContent :: Command { ref name, ref args } = message. content {
850888 let result = handle_command (
0 commit comments