diff --git a/indra/newview/llaisapi.cpp b/indra/newview/llaisapi.cpp
index d8588528c03a1816aaf2c50eb317da8779a07a1a..0adc7a703a3d30bd40bd6e99bd1306f530a3f2b6 100644
--- a/indra/newview/llaisapi.cpp
+++ b/indra/newview/llaisapi.cpp
@@ -452,6 +452,11 @@ void AISAPI::InvokeAISCommandCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t ht
 					AISUpdate::parseUUIDArray(result, "_created_categories", ids);
 				}
 				break;
+			case UPDATECATEGORY:
+				{
+					AISUpdate::parseUUIDArray(result, "_updated_categories", ids);
+				}
+				break;
 			default:
 				break;
 		}
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 873739f956e0005cb61ffda18cb956e7c618b559..5d14241c521ed04c08f553aba4819207862441dc 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -5470,6 +5470,10 @@ void LLAppViewer::disconnectViewer()
 	LLAppearanceMgr::instance().setAttachmentInvLinkEnable(false);
 // [/SL:KB]
 
+// [RLVa:KB] - Checked: RLVa-2.3 (Housekeeping)
+	SUBSYSTEM_CLEANUP(RlvHandler);
+// [/RLVa:KB]
+
 	gAgentWearables.cleanup();
 	gAgentCamera.cleanup();
 	// Also writes cached agent settings to gSavedSettings
diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp
index bf4e6b6542605e3e2a2f4b9af2c44ebeb8166936..eff069ae0ae8d15d230bf1e7eb25ae71516c5f45 100644
--- a/indra/newview/llinventoryfunctions.cpp
+++ b/indra/newview/llinventoryfunctions.cpp
@@ -381,7 +381,10 @@ void update_all_marketplace_count()
     return;
 }
 
-void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::string& new_name)
+//void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::string& new_name)
+// [RLVa:KB] - Checked: RLVa-2.3 (Give-to-#RLV)
+void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::string& new_name, LLPointer<LLInventoryCallback> cb)
+// [/RLVa:KB]
 {
 	LLViewerInventoryCategory* cat;
 
@@ -395,7 +398,10 @@ void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::s
 
 	LLSD updates;
 	updates["name"] = new_name;
-	update_inventory_category(cat_id, updates, NULL);
+// [RLVa:KB] - Checked: RLVa-2.3 (Give-to-#RLV)
+	update_inventory_category(cat_id, updates, cb);
+// [/RLVa:KB]
+//	update_inventory_category(cat_id, updates, NULL);
 }
 
 void copy_inventory_category(LLInventoryModel* model,
diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h
index fd106bc2d855b3730b34b22159673ea0a4968087..cb44d0966de86c0d1fb65fb6bbe19298ca20ccf8 100644
--- a/indra/newview/llinventoryfunctions.h
+++ b/indra/newview/llinventoryfunctions.h
@@ -68,7 +68,10 @@ void update_marketplace_category(const LLUUID& cat_id, bool perform_consistency_
 // Nudge all listing categories to signal that their marketplace status changed
 void update_all_marketplace_count();
 
-void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::string& new_name);
+// [RLVa:KB] - Checked: RLVa-2.3 (Give-to-#RLV)
+void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::string& new_name, LLPointer<LLInventoryCallback> cb = nullptr);
+// [/RLVa:KB]
+//void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::string& new_name);
 
 void copy_inventory_category(LLInventoryModel* model, LLViewerInventoryCategory* cat, const LLUUID& parent_id, const LLUUID& root_copy_id = LLUUID::null, bool move_no_copy_items = false);
 
diff --git a/indra/newview/rlvhandler.cpp b/indra/newview/rlvhandler.cpp
index 21c48421a6f7bc51a74963b3658be0124a1d6120..9de799967c5967a01ca4be92ecb293c492a62cd6 100644
--- a/indra/newview/rlvhandler.cpp
+++ b/indra/newview/rlvhandler.cpp
@@ -145,14 +145,58 @@ RlvHandler::RlvHandler() : m_fCanCancelTp(true), m_posSitSource(), m_pGCTimer(NU
 
 RlvHandler::~RlvHandler()
 {
+	cleanup();
+}
+
+void RlvHandler::cleanup()
+{
+	// Nothing to clean if we're not enabled (or already cleaned up)
+	if (!m_fEnabled)
+		return;
+
+	//
+	// Clean up any restrictions that are still active
+	//
+	RLV_ASSERT(LLApp::isQuitting());	// Several commands toggle debug settings but won't if they know the viewer is quitting
+
+	// Assume we have no way to predict how m_Objects will change so make a copy ahead of time
+	uuid_vec_t idRlvObjects;
+	idRlvObjects.reserve(m_Objects.size());
+	std::transform(m_Objects.begin(), m_Objects.end(), std::back_inserter(idRlvObjects), [](const rlv_object_map_t::value_type& kvPair) {return kvPair.first; });
+	for (const LLUUID & idRlvObj : idRlvObjects)
+	{
+		processCommand(idRlvObj, "clear", true);
+	}
+
+	// Sanity check
+	RLV_ASSERT(m_Objects.empty());
+	RLV_ASSERT(m_Exceptions.empty());
+	RLV_ASSERT(std::all_of(m_Behaviours, m_Behaviours + RLV_BHVR_COUNT, [](S16 cnt) { return !cnt; }));
+	RLV_ASSERT(m_CurCommandStack.empty());
+	RLV_ASSERT(m_CurObjectStack.empty());
+	RLV_ASSERT(m_pOverlayImage.isNull());
+
+	//
+	// Clean up what's left
+	//
 	gAgent.removeListener(this);
+	m_Retained.clear();
+	//delete m_pGCTimer;	// <- deletes itself
+
 	if (m_PendingGroupChange.first.notNull())
 	{
-		LLGroupMgr::instance().removeObserver(m_PendingGroupChange.first, this);
+		if (LLGroupMgr::instanceExists())
+			LLGroupMgr::instance().removeObserver(m_PendingGroupChange.first, this);
 		m_PendingGroupChange = std::make_pair(LLUUID::null, LLStringUtil::null);
 	}
 
-	//delete m_pGCTimer;	// <- deletes itself
+	for (RlvExtCommandHandler* pCmdHandler : m_CommandHandlers)
+	{
+		delete pCmdHandler;
+	}
+	m_CommandHandlers.clear();
+
+	m_fEnabled = false;
 }
 
 // ============================================================================
@@ -1032,6 +1076,12 @@ bool RlvHandler::onGC()
 	return (0 != m_Objects.size());	// GC will kill itself if it has nothing to do
 }
 
+// static
+void RlvHandler::cleanupClass()
+{
+	gRlvHandler.cleanup();
+}
+
 // Checked: 2009-11-26 (RLVa-1.1.0f) | Added: RLVa-1.1.0f
 void RlvHandler::onIdleStartup(void* pParam)
 {
diff --git a/indra/newview/rlvhandler.h b/indra/newview/rlvhandler.h
index 85acec1bcebc757a72b30b8a5892e23bafa4f999..5a6e5508eb55b8c87eb69983ad53d29db561d672 100644
--- a/indra/newview/rlvhandler.h
+++ b/indra/newview/rlvhandler.h
@@ -173,6 +173,7 @@ class RlvHandler : public LLOldEvents::LLSimpleListener, public LLParticularGrou
 
 	// Externally invoked event handlers
 public:
+	void cleanup();
 	void onActiveGroupChanged();
 	void onAttach(const LLViewerObject* pAttachObj, const LLViewerJointAttachment* pAttachPt);
 	void onDetach(const LLViewerObject* pAttachObj, const LLViewerJointAttachment* pAttachPt);
@@ -183,6 +184,7 @@ class RlvHandler : public LLOldEvents::LLSimpleListener, public LLParticularGrou
 	void onSitOrStand(bool fSitting);
 	void onTeleportFailed();
 	void onTeleportFinished(const LLVector3d& posArrival);
+	static void cleanupClass();
 	static void onIdleStartup(void* pParam);
 protected:
 	void getAttachmentResourcesCoro(const std::string& strUrl);
diff --git a/indra/newview/rlvinventory.cpp b/indra/newview/rlvinventory.cpp
index 082cc6065b5d1eeeb2ed9b7a260bb0192915e838..e3d06e2bdc67686bc13d3570751a6e76cf2a8e6b 100644
--- a/indra/newview/rlvinventory.cpp
+++ b/indra/newview/rlvinventory.cpp
@@ -578,36 +578,56 @@ void RlvGiveToRLVOffer::onCategoryCreateCallback(LLUUID idFolder, RlvGiveToRLVOf
 	pInstance->onDestinationCreated(idFolder, pInstance->m_DestPath.front());
 }
 
-// Checked: 2014-01-07 (RLVa-1.4.10)
-void RlvGiveToRLVOffer::moveAndRename(const LLUUID& idFolder, const LLUUID& idDestination, const std::string& strName)
+// static
+void RlvGiveToRLVOffer::moveAndRename(const LLUUID& idFolder, const LLUUID& idDestination, const std::string& strName, const LLPointer<LLInventoryCallback> cbFinal)
 {
-	const LLViewerInventoryCategory* pDest = gInventory.getCategory(idDestination);
 	const LLViewerInventoryCategory* pFolder = gInventory.getCategory(idFolder);
-	if ( (pDest) && (pFolder) )
+	if ( (idDestination.notNull()) && (pFolder) )
 	{
-		LLPointer<LLViewerInventoryCategory> pNewFolder = new LLViewerInventoryCategory(pFolder);
-		if (pDest->getUUID() != pFolder->getParentUUID())
+		bool needsRename = (pFolder->getName() != strName);
+
+		LLPointer<LLInventoryCallback> cbMove;
+		if (idDestination != pFolder->getParentUUID())
 		{
-			LLInventoryModel::update_list_t update;
-			LLInventoryModel::LLCategoryUpdate updOldParent(pFolder->getParentUUID(), -1);
-			update.push_back(updOldParent);
-			LLInventoryModel::LLCategoryUpdate updNewParent(pDest->getUUID(), 1);
-			update.push_back(updNewParent);
-			gInventory.accountForUpdate(update);
-
-			pNewFolder->setParent(pDest->getUUID());
-			pNewFolder->updateParentOnServer(FALSE);
-		}
+			// We have to move *after* the rename operation completes or AIS will drop it
+			if (!needsRename)
+			{
+				LLInventoryModel::update_list_t update;
+				LLInventoryModel::LLCategoryUpdate updOldParent(pFolder->getParentUUID(), -1);
+				update.push_back(updOldParent);
+				LLInventoryModel::LLCategoryUpdate updNewParent(idDestination, 1);
+				update.push_back(updNewParent);
+				gInventory.accountForUpdate(update);
 
-		pNewFolder->rename(strName);
-		pNewFolder->updateServer(FALSE);
-		gInventory.updateCategory(pNewFolder);
+				LLPointer<LLViewerInventoryCategory> pNewFolder = new LLViewerInventoryCategory(pFolder);
+				pNewFolder->setParent(idDestination);
+				pNewFolder->updateParentOnServer(FALSE);
 
-		gInventory.notifyObservers();
+				gInventory.updateCategory(pNewFolder);
+				gInventory.notifyObservers();
+
+				if (cbFinal)
+				{
+					cbFinal.get()->fire(idFolder);
+				}
+			}
+			else
+			{
+				cbMove = new LLBoostFuncInventoryCallback(boost::bind(RlvGiveToRLVOffer::moveAndRename, _1, idDestination, strName, cbFinal));
+			}
+		}
+
+		if (needsRename)
+		{
+			rename_category(&gInventory, idFolder, strName, (cbMove) ? cbMove : cbFinal);
+		}
+	}
+	else if (cbFinal)
+	{
+		cbFinal.get()->fire(LLUUID::null);
 	}
 }
 
-// Checked: 2010-04-18 (RLVa-1.2.0)
 void RlvGiveToRLVTaskOffer::changed(U32 mask)
 {
 	if (mask & LLInventoryObserver::ADD)
@@ -633,7 +653,6 @@ void RlvGiveToRLVTaskOffer::changed(U32 mask)
 	}
 }
 
-// Checked: 2010-04-18 (RLVa-1.2.0)
 void RlvGiveToRLVTaskOffer::done()
 {
 	gInventory.removeObserver(this);
@@ -642,22 +661,29 @@ void RlvGiveToRLVTaskOffer::done()
 	doOnIdleOneTime(boost::bind(&RlvGiveToRLVTaskOffer::doneIdle, this));
 }
 
-// Checked: 2014-01-07 (RLVa-1.4.10)
 void RlvGiveToRLVTaskOffer::doneIdle()
 {
-	const LLViewerInventoryCategory* pFolder = (m_Folders.size()) ? gInventory.getCategory(m_Folders.front()) : NULL;
+	const LLViewerInventoryCategory* pFolder = (m_Folders.size()) ? gInventory.getCategory(m_Folders.front()) : nullptr;
 	if ( (!pFolder) || (!createDestinationFolder(pFolder->getName())) )
 		delete this;
 }
 
-// Checked: 2010-04-18 (RLVa-1.2.0)
-void RlvGiveToRLVTaskOffer::onDestinationCreated(const LLUUID& idFolder, const std::string& strName)
+void RlvGiveToRLVTaskOffer::onDestinationCreated(const LLUUID& idDestFolder, const std::string& strName)
+{
+	if (const LLViewerInventoryCategory* pTarget = (idDestFolder.notNull()) ? gInventory.getCategory(idDestFolder) : nullptr)
+	{
+		moveAndRename(m_Folders.front(), idDestFolder, strName, new LLBoostFuncInventoryCallback(boost::bind(&RlvGiveToRLVTaskOffer::onOfferCompleted, this, _1)));
+	}
+	else
+	{
+		onOfferCompleted(LLUUID::null);
+	}
+}
+
+void RlvGiveToRLVTaskOffer::onOfferCompleted(const LLUUID& idOfferedFolder)
 {
-	const LLViewerInventoryCategory* pTarget = (idFolder.notNull()) ? gInventory.getCategory(idFolder) : NULL;
-	if (pTarget)
+	if (idOfferedFolder.notNull())
 	{
-		const LLUUID& idOfferedFolder = m_Folders.front();
-		moveAndRename(idOfferedFolder, idFolder, strName);
 		RlvBehaviourNotifyHandler::sendNotification("accepted_in_rlv inv_offer " + RlvInventory::instance().getSharedPath(idOfferedFolder));
 	}
 	delete this;
@@ -684,7 +710,7 @@ void RlvGiveToRLVAgentOffer::doneIdle()
 void RlvGiveToRLVAgentOffer::onDestinationCreated(const LLUUID& idFolder, const std::string& strName)
 {
 	if ( (idFolder.notNull()) && (mComplete.size()) )
-		moveAndRename(mComplete[0], idFolder, strName);
+		moveAndRename(mComplete[0], idFolder, strName, nullptr);
 	delete this;
 }
 
diff --git a/indra/newview/rlvinventory.h b/indra/newview/rlvinventory.h
index 1279dff2f85bdbb670050ac848df3066ee773cec..2cb7f8bc8edac693c8a7cc9a22831631597f685d 100644
--- a/indra/newview/rlvinventory.h
+++ b/indra/newview/rlvinventory.h
@@ -131,8 +131,8 @@ class RlvGiveToRLVOffer
 	virtual ~RlvGiveToRLVOffer() {}
 protected:
 	bool         createDestinationFolder(const std::string& strPath);
-	virtual void onDestinationCreated(const LLUUID& idFolder, const std::string& strName) = 0;
-	void         moveAndRename(const LLUUID& idFolder, const LLUUID& idDestination, const std::string& strName);
+	virtual void onDestinationCreated(const LLUUID& idDestFolder, const std::string& strName) = 0;
+	static void  moveAndRename(const LLUUID& idFolder, const LLUUID& idDestination, const std::string& strName, LLPointer<LLInventoryCallback> cb);
 private:
 	static void  onCategoryCreateCallback(LLUUID idFolder, RlvGiveToRLVOffer* pInstance);
 
@@ -147,11 +147,12 @@ class RlvGiveToRLVTaskOffer : public LLInventoryObserver, RlvGiveToRLVOffer
 {
 public:
 	RlvGiveToRLVTaskOffer(const LLUUID& idTransaction) : RlvGiveToRLVOffer(), m_idTransaction(idTransaction) {}
-	/*virtual*/ void changed(U32 mask);
+	void changed(U32 mask) override;
 protected:
-	/*virtual*/ void done();
-	            void doneIdle();
-	/*virtual*/ void onDestinationCreated(const LLUUID& idFolder, const std::string& strName);
+	void done();
+	void doneIdle();
+	void onDestinationCreated(const LLUUID& idDestFolder, const std::string& strName) override;
+	void onOfferCompleted(const LLUUID& idOfferedFolder);
 
 protected:
 	typedef std::vector<LLUUID> folder_ref_t;