@@ -1168,12 +1168,18 @@ def _normalize_plan_view_mode(value: Any) -> str:
11681168 return mode
11691169 return "view"
11701170
1171+ @staticmethod
1172+ def _coerce_json_dict (value : Any ) -> dict [str , Any ]:
1173+ if not isinstance (value , dict ):
1174+ return {}
1175+ return {str (key ): item for key , item in value .items ()}
1176+
11711177 def _get_plan_view_mode_preference (self ) -> str :
11721178 user = self ._get_current_user_account ()
11731179 if user is None :
11741180 return "view"
1175- config = user . frontend_multi_user_config if isinstance (user .frontend_multi_user_config , dict ) else {}
1176- plan_page = config . get ( "plan_page" ) if isinstance ( config .get ("plan_page" ), dict ) else {}
1181+ config = self . _coerce_json_dict (user .frontend_multi_user_config )
1182+ plan_page = self . _coerce_json_dict ( config .get ("plan_page" ))
11771183 return self ._normalize_plan_view_mode (plan_page .get ("selected_segment" ))
11781184
11791185 def _set_plan_view_mode_preference (self , mode : str ) -> None :
@@ -1183,9 +1189,9 @@ def _set_plan_view_mode_preference(self, mode: str) -> None:
11831189 normalized_mode = self ._normalize_plan_view_mode (mode )
11841190 # JSON columns are not mutation-tracked by default; build fresh dicts so
11851191 # SQLAlchemy sees a new value and persists the update reliably.
1186- existing_config = user . frontend_multi_user_config if isinstance (user .frontend_multi_user_config , dict ) else {}
1192+ existing_config = self . _coerce_json_dict (user .frontend_multi_user_config )
11871193 config = dict (existing_config )
1188- existing_plan_page = config . get ( "plan_page" ) if isinstance ( config .get ("plan_page" ), dict ) else {}
1194+ existing_plan_page = self . _coerce_json_dict ( config .get ("plan_page" ))
11891195 plan_page = dict (existing_plan_page )
11901196 plan_page ["selected_segment" ] = normalized_mode
11911197 config ["plan_page" ] = plan_page
@@ -1547,7 +1553,7 @@ def _build_plan_failure_trace(self, task: TaskItem) -> dict[str, Any]:
15471553
15481554 return failure_trace
15491555
1550- def _build_plan_telemetry_cache_key (self , task : TaskItem , include_raw : bool ) -> Optional [tuple [str , str , bool ]]:
1556+ def _build_plan_telemetry_cache_key (self , task : TaskItem , include_raw : bool ) -> Optional [tuple [str , str , bool , bool ]]:
15511557 state = task .state if isinstance (task .state , TaskState ) else None
15521558 if include_raw or state not in (TaskState .completed , TaskState .failed ):
15531559 return None
@@ -1969,7 +1975,7 @@ def inject_current_user_name():
19691975 @self .app .route ('/' )
19701976 def index ():
19711977 user = None
1972- recent_tasks : list [TaskItem ] = []
1978+ recent_tasks : list [SimpleNamespace ] = []
19731979 total_tasks_count = 0
19741980 is_admin = False
19751981 nonce = None
0 commit comments