diff --git a/.hgtags b/.hgtags
index feff45e7d87d97e9208ba30ae0b3bb7235e355f0..a19c9d259c7211b0a51594d5db6c20d857eae3d0 100755
--- a/.hgtags
+++ b/.hgtags
@@ -504,3 +504,4 @@ afd8d4756e8eda3c8f760625d1c17a2ad40ad6c8 3.7.27-release
 d07f76c5b9860fb87924d00ca729f7d4532534d6 3.7.29-release
 67edc442c80b8d2fadd2a6c4a7184b469906cdbf 3.7.30-release
 797ed69e6134ef48bb922577ab2540fb2d964668 3.8.0-release
+3f61ed662347dc7c6941b8266e72746a66d90e2a 3.8.1-release
diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp
index 2100989316446e087dc021711f70f357c5d37320..5ed348e13cf178b1a335abe20dd6af1d014163cb 100755
--- a/indra/llcommon/llerror.cpp
+++ b/indra/llcommon/llerror.cpp
@@ -119,6 +119,7 @@ namespace {
 				LL_INFOS() << "Error setting log file to " << filename << LL_ENDL;
 			}
 			mWantsTime = true;
+            mWantsTags = true;
 		}
 		
 		~RecordToFile()
@@ -558,7 +559,7 @@ namespace LLError
 		mFunctionString += std::string(mFunction) + ":";
 		for (size_t i = 0; i < mTagCount; i++)
 		{
-			mTagString += std::string("#") + mTags[i] + ((i == mTagCount - 1) ? "" : " ");
+			mTagString += std::string("#") + mTags[i] + ((i == mTagCount - 1) ? "" : ",");
 		}
 	}
 
@@ -931,14 +932,19 @@ namespace
 			}
 
 			if (show_level && r->wantsLevel())
-				{
-				message_stream << site.mLevelString << " ";
-				}
+            {
+				message_stream << site.mLevelString;
+            }
 				
 			if (show_tags && r->wantsTags())
 			{
-				message_stream << site.mTagString << " ";
+				message_stream << site.mTagString;
 			}
+			if ((show_level && r->wantsLevel())||
+                (show_tags && r->wantsTags()))
+            {
+                message_stream << " ";
+            }
 
 			if (show_function && r->wantsFunctionName())
 			{
diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt
index f2807196747ffcee4ae8a36604b9cff7ebeed9ca..a08ffae0caee5838d948ba0de742f5f768312c2a 100644
--- a/indra/newview/VIEWER_VERSION.txt
+++ b/indra/newview/VIEWER_VERSION.txt
@@ -1 +1 @@
-3.8.1
+3.8.2
diff --git a/indra/newview/llagentwearables.cpp b/indra/newview/llagentwearables.cpp
index 13b16f52183730d60b5c92bfc263ba5d1bf08562..cce1eb589592814ab2849f3b72235227dc746679 100755
--- a/indra/newview/llagentwearables.cpp
+++ b/indra/newview/llagentwearables.cpp
@@ -27,6 +27,7 @@
 #include "llviewerprecompiledheaders.h"
 #include "llagentwearables.h"
 
+#include "llattachmentsmgr.h"
 #include "llaccordionctrltab.h"
 #include "llagent.h"
 #include "llagentcamera.h"
@@ -1370,6 +1371,7 @@ void LLAgentWearables::userRemoveMultipleAttachments(llvo_vec_t& objects_to_remo
 	if (objects_to_remove.empty())
 		return;
 
+	LL_DEBUGS("Avatar") << "ATT [ObjectDetach] removing " << objects_to_remove.size() << " objects" << LL_ENDL;
 	gMessageSystem->newMessage("ObjectDetach");
 	gMessageSystem->nextBlockFast(_PREHASH_AgentData);
 	gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
@@ -1383,6 +1385,10 @@ void LLAgentWearables::userRemoveMultipleAttachments(llvo_vec_t& objects_to_remo
 		//gAgentAvatarp->resetJointPositionsOnDetach(objectp);
 		gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
 		gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, objectp->getLocalID());
+		const LLUUID& item_id = objectp->getAttachmentItemID();
+		LLViewerInventoryItem *item = gInventory.getItem(item_id);
+		LL_DEBUGS("Avatar") << "ATT removing object, item is " << (item ? item->getName() : "UNKNOWN") << " " << item_id << LL_ENDL;
+        LLAttachmentsMgr::instance().onDetachRequested(item_id);
 	}
 	gMessageSystem->sendReliable(gAgent.getRegionHost());
 }
@@ -1391,51 +1397,18 @@ void LLAgentWearables::userAttachMultipleAttachments(LLInventoryModel::item_arra
 {
 	// Build a compound message to send all the objects that need to be rezzed.
 	S32 obj_count = obj_item_array.size();
-
-	// Limit number of packets to send
-	const S32 MAX_PACKETS_TO_SEND = 10;
-	const S32 OBJECTS_PER_PACKET = 4;
-	const S32 MAX_OBJECTS_TO_SEND = MAX_PACKETS_TO_SEND * OBJECTS_PER_PACKET;
-	if( obj_count > MAX_OBJECTS_TO_SEND )
+	if (obj_count > 0)
 	{
-		obj_count = MAX_OBJECTS_TO_SEND;
+		LL_DEBUGS("Avatar") << "ATT attaching multiple, total obj_count " << obj_count << LL_ENDL;
 	}
-				
-	// Create an id to keep the parts of the compound message together
-	LLUUID compound_msg_id;
-	compound_msg_id.generate();
-	LLMessageSystem* msg = gMessageSystem;
-
-	for(S32 i = 0; i < obj_count; ++i)
-	{
-		if( 0 == (i % OBJECTS_PER_PACKET) )
-		{
-			// Start a new message chunk
-			msg->newMessageFast(_PREHASH_RezMultipleAttachmentsFromInv);
-			msg->nextBlockFast(_PREHASH_AgentData);
-			msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
-			msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
-			msg->nextBlockFast(_PREHASH_HeaderData);
-			msg->addUUIDFast(_PREHASH_CompoundMsgID, compound_msg_id );
-			msg->addU8Fast(_PREHASH_TotalObjects, obj_count );
-			msg->addBOOLFast(_PREHASH_FirstDetachAll, false );
-		}
 
-		const LLInventoryItem* item = obj_item_array.at(i).get();
-		msg->nextBlockFast(_PREHASH_ObjectData );
-		msg->addUUIDFast(_PREHASH_ItemID, item->getLinkedUUID());
-		msg->addUUIDFast(_PREHASH_OwnerID, item->getPermissions().getOwner());
-		msg->addU8Fast(_PREHASH_AttachmentPt, 0 | ATTACHMENT_ADD);	// Wear at the previous or default attachment point
-		pack_permissions_slam(msg, item->getFlags(), item->getPermissions());
-		msg->addStringFast(_PREHASH_Name, item->getName());
-		msg->addStringFast(_PREHASH_Description, item->getDescription());
-
-		if( (i+1 == obj_count) || ((OBJECTS_PER_PACKET-1) == (i % OBJECTS_PER_PACKET)) )
-		{
-			// End of message chunk
-			msg->sendReliable( gAgent.getRegion()->getHost() );
-		}
-	}
+    for(LLInventoryModel::item_array_t::const_iterator it = obj_item_array.begin();
+        it != obj_item_array.end();
+        ++it)
+    {
+		const LLInventoryItem* item = *it;
+        LLAttachmentsMgr::instance().addAttachmentRequest(item->getLinkedUUID(), 0, TRUE);
+    }
 }
 
 // Returns false if the given wearable is already topmost/bottommost
diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp
index 4016529092764dc80736b4939c1ec6b3f5c365c7..142a3250c896d365d5abf7d52ce2b53a13a7134d 100755
--- a/indra/newview/llappearancemgr.cpp
+++ b/indra/newview/llappearancemgr.cpp
@@ -525,6 +525,15 @@ LLUpdateAppearanceAndEditWearableOnDestroy::LLUpdateAppearanceAndEditWearableOnD
 {
 }
 
+LLRequestServerAppearanceUpdateOnDestroy::~LLRequestServerAppearanceUpdateOnDestroy()
+{
+	LL_DEBUGS("Avatar") << "ATT requesting server appearance update" << LL_ENDL;
+    if (!LLApp::isExiting())
+    {
+        LLAppearanceMgr::instance().requestServerAppearanceUpdate();
+    }
+}
+
 void edit_wearable_and_customize_avatar(LLUUID item_id)
 {
 	// Start editing the item if previously requested.
@@ -828,6 +837,12 @@ void LLWearableHoldingPattern::onAllComplete()
 		// pre-attachment states.
 		gAgentAvatarp->clearAttachmentPosOverrides();
 
+		if (objects_to_remove.size() || items_to_add.size())
+		{
+			LL_DEBUGS("Avatar") << "ATT will remove " << objects_to_remove.size()
+								<< " and add " << items_to_add.size() << " items" << LL_ENDL;
+		}
+
 		// Take off the attachments that will no longer be in the outfit.
 		LLAgentWearables::userRemoveMultipleAttachments(objects_to_remove);
 		
@@ -1356,28 +1371,46 @@ void LLAppearanceMgr::wearItemsOnAvatar(const uuid_vec_t& item_ids_to_wear,
 
         const LLUUID& item_id_to_wear = *it;
 
-        if (item_id_to_wear.isNull()) continue;
+        if (item_id_to_wear.isNull())
+        {
+            LL_DEBUGS("Avatar") << "null id " << item_id_to_wear << LL_ENDL;
+            continue;
+        }
 
         LLViewerInventoryItem* item_to_wear = gInventory.getItem(item_id_to_wear);
-        if (!item_to_wear) continue;
+        if (!item_to_wear)
+        {
+            LL_DEBUGS("Avatar") << "inventory item not found for id " << item_id_to_wear << LL_ENDL;
+            continue;
+        }
 
         if (gInventory.isObjectDescendentOf(item_to_wear->getUUID(), gInventory.getLibraryRootFolderID()))
         {
+            LL_DEBUGS("Avatar") << "inventory item in library, will copy and wear "
+                                << item_to_wear->getName() << " id " << item_id_to_wear << LL_ENDL;
             LLPointer<LLInventoryCallback> cb = new LLBoostFuncInventoryCallback(boost::bind(wear_on_avatar_cb,_1,replace));
-            copy_inventory_item(gAgent.getID(), item_to_wear->getPermissions().getOwner(), item_to_wear->getUUID(), LLUUID::null, std::string(), cb);
+            copy_inventory_item(gAgent.getID(), item_to_wear->getPermissions().getOwner(),
+                                item_to_wear->getUUID(), LLUUID::null, std::string(), cb);
             continue;
         } 
         else if (!gInventory.isObjectDescendentOf(item_to_wear->getUUID(), gInventory.getRootFolderID()))
         {
-            continue; // not in library and not in agent's inventory
+			// not in library and not in agent's inventory
+            LL_DEBUGS("Avatar") << "inventory item not in user inventory or library, skipping "
+                                << item_to_wear->getName() << " id " << item_id_to_wear << LL_ENDL;
+            continue; 
         }
         else if (gInventory.isObjectDescendentOf(item_to_wear->getUUID(), gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH)))
         {
             LLNotificationsUtil::add("CannotWearTrash");
+            LL_DEBUGS("Avatar") << "inventory item is in trash, skipping "
+                                << item_to_wear->getName() << " id " << item_id_to_wear << LL_ENDL;
             continue;
         }
         else if (isLinkedInCOF(item_to_wear->getUUID())) // EXT-84911
         {
+            LL_DEBUGS("Avatar") << "inventory item is already in COF, skipping "
+                                << item_to_wear->getName() << " id " << item_id_to_wear << LL_ENDL;
             continue;
         }
 
@@ -2721,28 +2754,23 @@ void LLAppearanceMgr::addCOFItemLink(const LLInventoryItem *item,
 
 LLInventoryModel::item_array_t LLAppearanceMgr::findCOFItemLinks(const LLUUID& item_id)
 {
-
 	LLInventoryModel::item_array_t result;
-	const LLViewerInventoryItem *vitem =
-		dynamic_cast<const LLViewerInventoryItem*>(gInventory.getItem(item_id));
 
-	if (vitem)
-	{
-		LLInventoryModel::cat_array_t cat_array;
-		LLInventoryModel::item_array_t item_array;
-		gInventory.collectDescendents(LLAppearanceMgr::getCOF(),
-									  cat_array,
-									  item_array,
-									  LLInventoryModel::EXCLUDE_TRASH);
-		for (S32 i=0; i<item_array.size(); i++)
-		{
-			const LLViewerInventoryItem* inv_item = item_array.at(i).get();
-			if (inv_item->getLinkedUUID() == vitem->getLinkedUUID())
-			{
-				result.push_back(item_array.at(i));
-			}
-		}
-	}
+    LLUUID linked_id = gInventory.getLinkedItemID(item_id);
+    LLInventoryModel::cat_array_t cat_array;
+    LLInventoryModel::item_array_t item_array;
+    gInventory.collectDescendents(LLAppearanceMgr::getCOF(),
+                                  cat_array,
+                                  item_array,
+                                  LLInventoryModel::EXCLUDE_TRASH);
+    for (S32 i=0; i<item_array.size(); i++)
+    {
+        const LLViewerInventoryItem* inv_item = item_array.at(i).get();
+        if (inv_item->getLinkedUUID() == linked_id)
+        {
+            result.push_back(item_array.at(i));
+        }
+    }
 	return result;
 }
 
@@ -3319,7 +3347,7 @@ void RequestAgentUpdateAppearanceResponder::onRequestRequested()
 	}
 
 	// Actually send the request.
-	LL_DEBUGS("Avatar") << "Will send request for cof_version " << cof_version << LL_ENDL;
+	LL_DEBUGS("Avatar") << "ATT sending bake request for cof_version " << cof_version << LL_ENDL;
 	mRetryPolicy->reset();
 	sendRequest();
 }
@@ -3792,6 +3820,11 @@ void LLAppearanceMgr::removeItemsFromAvatar(const uuid_vec_t& ids_to_remove)
 	{
 		const LLUUID& id_to_remove = *it;
 		const LLUUID& linked_item_id = gInventory.getLinkedItemID(id_to_remove);
+		LLViewerInventoryItem *item = gInventory.getItem(linked_item_id);
+		if (item && item->getType() == LLAssetType::AT_OBJECT)
+		{
+			LL_DEBUGS("Avatar") << "ATT removing attachment " << item->getName() << " id " << item->getUUID() << LL_ENDL;
+		}
 		removeCOFItemLinks(linked_item_id, cb);
 		addDoomedTempAttachment(linked_item_id);
 	}
@@ -3799,10 +3832,9 @@ void LLAppearanceMgr::removeItemsFromAvatar(const uuid_vec_t& ids_to_remove)
 
 void LLAppearanceMgr::removeItemFromAvatar(const LLUUID& id_to_remove)
 {
-	LLUUID linked_item_id = gInventory.getLinkedItemID(id_to_remove);
-	LLPointer<LLInventoryCallback> cb = new LLUpdateAppearanceOnDestroy;
-	removeCOFItemLinks(linked_item_id, cb);
-	addDoomedTempAttachment(linked_item_id);
+	uuid_vec_t ids_to_remove;
+	ids_to_remove.push_back(id_to_remove);
+	removeItemsFromAvatar(ids_to_remove);
 }
 
 
@@ -3987,37 +4019,32 @@ void dumpAttachmentSet(const std::set<LLUUID>& atts, const std::string& msg)
 
 void LLAppearanceMgr::registerAttachment(const LLUUID& item_id)
 {
-	   gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id);
-
-	   if (mAttachmentInvLinkEnabled)
-	   {
-		   // we have to pass do_update = true to call LLAppearanceMgr::updateAppearanceFromCOF.
-		   // it will trigger gAgentWariables.notifyLoadingFinished()
-		   // But it is not acceptable solution. See EXT-7777
-		   if (!isLinkedInCOF(item_id))
-		   {
-			   LLPointer<LLInventoryCallback> cb = new LLUpdateAppearanceOnDestroy();
-			   LLAppearanceMgr::addCOFItemLink(item_id, cb);  // Add COF link for item.
-		   }
-	   }
-	   else
-	   {
-		   //LL_INFOS() << "no link changes, inv link not enabled" << LL_ENDL;
-	   }
+	LLViewerInventoryItem *item = gInventory.getItem(item_id);
+	LL_DEBUGS("Avatar") << "ATT registering attachment "
+						<< (item ? item->getName() : "UNKNOWN") << " " << item_id << LL_ENDL;
+	gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id);
+
+	LLAttachmentsMgr::instance().onAttachmentArrived(item_id);
 }
 
 void LLAppearanceMgr::unregisterAttachment(const LLUUID& item_id)
 {
-	   gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id);
+	LLViewerInventoryItem *item = gInventory.getItem(item_id);
+	LL_DEBUGS("Avatar") << "ATT unregistering attachment "
+						<< (item ? item->getName() : "UNKNOWN") << " " << item_id << LL_ENDL;
+	gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id);
 
-	   if (mAttachmentInvLinkEnabled)
-	   {
-		   LLAppearanceMgr::removeCOFItemLinks(item_id);
-	   }
-	   else
-	   {
-		   //LL_INFOS() << "no link changes, inv link not enabled" << LL_ENDL;
-	   }
+    LLAttachmentsMgr::instance().onDetachCompleted(item_id);
+	if (mAttachmentInvLinkEnabled && isLinkedInCOF(item_id))
+	{
+		LL_DEBUGS("Avatar") << "ATT removing COF link for attachment "
+							<< (item ? item->getName() : "UNKNOWN") << " " << item_id << LL_ENDL;
+		LLAppearanceMgr::removeCOFItemLinks(item_id);
+	}
+	else
+	{
+		//LL_INFOS() << "no link changes, inv link not enabled" << LL_ENDL;
+	}
 }
 
 BOOL LLAppearanceMgr::getIsInCOF(const LLUUID& obj_id) const
@@ -4031,14 +4058,6 @@ BOOL LLAppearanceMgr::getIsInCOF(const LLUUID& obj_id) const
 	return FALSE;
 }
 
-// static
-bool LLAppearanceMgr::isLinkInCOF(const LLUUID& obj_id)
-{
-	const LLUUID& target_id = gInventory.getLinkedItemID(obj_id);
-	LLLinkedItemIDMatches find_links(target_id);
-	return gInventory.hasMatchingDirectDescendent(LLAppearanceMgr::instance().getCOF(), find_links);
-}
-
 BOOL LLAppearanceMgr::getIsProtectedCOFItem(const LLUUID& obj_id) const
 {
 	if (!getIsInCOF(obj_id)) return FALSE;
@@ -4157,10 +4176,56 @@ void callAfterCategoryFetch(const LLUUID& cat_id, nullary_func_t cb)
 	}
 }
 
+void add_wearable_type_counts(const uuid_vec_t& ids,
+                              S32& clothing_count,
+                              S32& bodypart_count,
+                              S32& object_count,
+                              S32& other_count)
+{
+    for (uuid_vec_t::const_iterator it = ids.begin(); it != ids.end(); ++it)
+    {
+        const LLUUID& item_id_to_wear = *it;
+        LLViewerInventoryItem* item_to_wear = gInventory.getItem(item_id_to_wear);
+        if (item_to_wear)
+        {
+            if (item_to_wear->getType() == LLAssetType::AT_CLOTHING)
+            {
+                clothing_count++;
+            }
+            else if (item_to_wear->getType() == LLAssetType::AT_BODYPART)
+            {
+                bodypart_count++;
+            }
+            else if (item_to_wear->getType() == LLAssetType::AT_OBJECT)
+            {
+                object_count++;
+            }
+            else
+            {
+                other_count++;
+            }
+        }
+        else
+        {
+            other_count++;
+        }
+    }
+}
+
 void wear_multiple(const uuid_vec_t& ids, bool replace)
 {
-	LLPointer<LLInventoryCallback> cb = new LLUpdateAppearanceOnDestroy;
-    LLAppearanceMgr::instance().wearItemsOnAvatar(ids, false, replace, cb);
+    S32 clothing_count = 0;
+    S32 bodypart_count = 0;
+    S32 object_count = 0;
+    S32 other_count = 0;
+    add_wearable_type_counts(ids, clothing_count, bodypart_count, object_count, other_count);
+
+    LLPointer<LLInventoryCallback> cb = NULL;
+    if (clothing_count > 0 || bodypart_count > 0)
+    {
+        cb = new LLUpdateAppearanceOnDestroy;
+    }
+	LLAppearanceMgr::instance().wearItemsOnAvatar(ids, true, replace, cb);
 }
 
 // SLapp for easy-wearing of a stock (library) avatar
diff --git a/indra/newview/llappearancemgr.h b/indra/newview/llappearancemgr.h
index ee9d3b7209b85ec3c884581b2649067267063f90..4ed8c1bfb912c3caa1ed2f8bbe4f7e055a563bb4 100755
--- a/indra/newview/llappearancemgr.h
+++ b/indra/newview/llappearancemgr.h
@@ -284,11 +284,6 @@ class LLAppearanceMgr: public LLSingleton<LLAppearanceMgr>
 	BOOL getIsInCOF(const LLUUID& obj_id) const;
 	// Is this in the COF and can the user delete it from the COF?
 	BOOL getIsProtectedCOFItem(const LLUUID& obj_id) const;
-
-	/**
-	 * Checks if COF contains link to specified object.
-	 */
-	static bool isLinkInCOF(const LLUUID& obj_id);
 };
 
 class LLUpdateAppearanceOnDestroy: public LLInventoryCallback
@@ -320,6 +315,15 @@ class LLUpdateAppearanceAndEditWearableOnDestroy: public LLInventoryCallback
 	LLUUID mItemID;
 };
 
+class LLRequestServerAppearanceUpdateOnDestroy: public LLInventoryCallback
+{
+public:
+	LLRequestServerAppearanceUpdateOnDestroy() {}
+	~LLRequestServerAppearanceUpdateOnDestroy();
+
+	/* virtual */ void fire(const LLUUID& item_id) {}
+};
+
 LLUUID findDescendentCategoryIDByName(const LLUUID& parent_id,const std::string& name);
 
 // Invoke a given callable after category contents are fully fetched.
diff --git a/indra/newview/llattachmentsmgr.cpp b/indra/newview/llattachmentsmgr.cpp
index 256980eb04a29db249fbb3b83f001f710464da3c..2a137cc39b8234d66e4d55bd40081972fbecd784 100755
--- a/indra/newview/llattachmentsmgr.cpp
+++ b/indra/newview/llattachmentsmgr.cpp
@@ -27,15 +27,24 @@
 #include "llviewerprecompiledheaders.h"
 #include "llattachmentsmgr.h"
 
+#include "llvoavatarself.h"
 #include "llagent.h"
+#include "llappearancemgr.h"
 #include "llinventorymodel.h"
 #include "lltooldraganddrop.h" // pack_permissions_slam
 #include "llviewerinventory.h"
 #include "llviewerregion.h"
 #include "message.h"
 
+const F32 COF_LINK_BATCH_TIME = 5.0F;
+const F32 MAX_ATTACHMENT_REQUEST_LIFETIME = 30.0F;
+const F32 MIN_RETRY_REQUEST_TIME = 5.0F;
+const F32 MAX_BAD_COF_TIME = 30.0F;
 
-LLAttachmentsMgr::LLAttachmentsMgr()
+LLAttachmentsMgr::LLAttachmentsMgr():
+    mAttachmentRequests("attach",MIN_RETRY_REQUEST_TIME),
+    mDetachRequests("detach",MIN_RETRY_REQUEST_TIME),
+    mQuestionableCOFLinks("badcof",MAX_BAD_COF_TIME)
 {
 }
 
@@ -43,15 +52,37 @@ LLAttachmentsMgr::~LLAttachmentsMgr()
 {
 }
 
-void LLAttachmentsMgr::addAttachment(const LLUUID& item_id,
-									 const U8 attachment_pt,
-									 const BOOL add)
+void LLAttachmentsMgr::addAttachmentRequest(const LLUUID& item_id,
+                                            const U8 attachment_pt,
+                                            const BOOL add)
 {
+	LLViewerInventoryItem *item = gInventory.getItem(item_id);
+
+    if (mAttachmentRequests.wasRequestedRecently(item_id))
+    {
+        LL_DEBUGS("Avatar") << "ATT not adding attachment to mPendingAttachments, recent request is already pending: "
+                            << (item ? item->getName() : "UNKNOWN") << " id " << item_id << LL_ENDL;
+        return;
+    }
+
+	LL_DEBUGS("Avatar") << "ATT adding attachment to mPendingAttachments "
+						<< (item ? item->getName() : "UNKNOWN") << " id " << item_id << LL_ENDL;
+
 	AttachmentsInfo attachment;
 	attachment.mItemID = item_id;
 	attachment.mAttachmentPt = attachment_pt;
 	attachment.mAdd = add;
 	mPendingAttachments.push_back(attachment);
+
+    mAttachmentRequests.addTime(item_id);
+}
+
+void LLAttachmentsMgr::onAttachmentRequested(const LLUUID& item_id)
+{
+	LLViewerInventoryItem *item = gInventory.getItem(item_id);
+	LL_DEBUGS("Avatar") << "ATT attachment was requested "
+						<< (item ? item->getName() : "UNKNOWN") << " id " << item_id << LL_ENDL;
+    mAttachmentRequests.addTime(item_id);
 }
 
 // static
@@ -68,31 +99,79 @@ void LLAttachmentsMgr::onIdle()
 		return;
 	}
 
-	S32 obj_count = mPendingAttachments.size();
+    if (LLApp::isExiting())
+    {
+        return;
+    }
+
+	requestPendingAttachments();
+
+    linkRecentlyArrivedAttachments();
+
+    expireOldAttachmentRequests();
+
+    expireOldDetachRequests();
+
+    checkInvalidCOFLinks();
+    
+    spamStatusInfo();
+}
+
+void LLAttachmentsMgr::requestPendingAttachments()
+{
+	if (mPendingAttachments.size())
+	{
+		requestAttachments(mPendingAttachments);
+	}
+}
+
+// Send request(s) for a group of attachments. As coded, this can
+// request at most 40 attachments and the rest will be
+// ignored. Currently the max attachments per avatar is 38, so the 40
+// limit should not be hit in practice.
+void LLAttachmentsMgr::requestAttachments(attachments_vec_t& attachment_requests)
+{
+	// Make sure we got a region before trying anything else
+	if( !gAgent.getRegion() )
+	{
+		return;
+	}
+
+    // For unknown reasons, requesting many attachments at once causes
+    // frequent server-side failures. Here we're limiting the number
+    // of attachments requested per idle loop.
+    const S32 max_objects_per_request = 5;
+	S32 obj_count = llmin((S32)attachment_requests.size(),max_objects_per_request);
 	if (obj_count == 0)
 	{
 		return;
 	}
-	
+
 	// Limit number of packets to send
 	const S32 MAX_PACKETS_TO_SEND = 10;
 	const S32 OBJECTS_PER_PACKET = 4;
 	const S32 MAX_OBJECTS_TO_SEND = MAX_PACKETS_TO_SEND * OBJECTS_PER_PACKET;
 	if( obj_count > MAX_OBJECTS_TO_SEND )
 	{
+        LL_WARNS() << "ATT Too many attachments requested: " << obj_count
+                   << " exceeds limit of " << MAX_OBJECTS_TO_SEND << LL_ENDL;
+
 		obj_count = MAX_OBJECTS_TO_SEND;
 	}
 
+	LL_DEBUGS("Avatar") << "ATT [RezMultipleAttachmentsFromInv] attaching multiple from attachment_requests,"
+		" total obj_count " << obj_count << LL_ENDL;
+
 	LLUUID compound_msg_id;
 	compound_msg_id.generate();
 	LLMessageSystem* msg = gMessageSystem;
 
-	
-	S32 i = 0;
-	for (attachments_vec_t::const_iterator iter = mPendingAttachments.begin();
-		 iter != mPendingAttachments.end();
-		 ++iter)
-	{
+    // by construction above, obj_count <= attachment_requests.size(), so no
+    // check against attachment_requests.empty() is needed.
+    llassert(obj_count <= attachment_requests.size());
+
+    for (S32 i=0; i<obj_count; i++)
+    {
 		if( 0 == (i % OBJECTS_PER_PACKET) )
 		{
 			// Start a new message chunk
@@ -106,32 +185,337 @@ void LLAttachmentsMgr::onIdle()
 			msg->addBOOLFast(_PREHASH_FirstDetachAll, false );
 		}
 
-		const AttachmentsInfo &attachment = (*iter);
+		const AttachmentsInfo& attachment = attachment_requests.front();
 		LLViewerInventoryItem* item = gInventory.getItem(attachment.mItemID);
-		if (!item)
+		if (item)
+        {
+            LL_DEBUGS("Avatar") << "ATT requesting from attachment_requests " << item->getName()
+                                << " " << item->getLinkedUUID() << LL_ENDL;
+            S32 attachment_pt = attachment.mAttachmentPt;
+            if (attachment.mAdd) 
+                attachment_pt |= ATTACHMENT_ADD;
+            
+            msg->nextBlockFast(_PREHASH_ObjectData );
+            msg->addUUIDFast(_PREHASH_ItemID, item->getLinkedUUID());
+            msg->addUUIDFast(_PREHASH_OwnerID, item->getPermissions().getOwner());
+            msg->addU8Fast(_PREHASH_AttachmentPt, attachment_pt);
+            pack_permissions_slam(msg, item->getFlags(), item->getPermissions());
+            msg->addStringFast(_PREHASH_Name, item->getName());
+            msg->addStringFast(_PREHASH_Description, item->getDescription());
+        }
+        else
 		{
-			LL_INFOS() << "Attempted to add non-existant item ID:" << attachment.mItemID << LL_ENDL;
-			continue;
+			LL_WARNS("Avatar") << "ATT Attempted to add non-existent item ID:" << attachment.mItemID << LL_ENDL;
 		}
-		S32 attachment_pt = attachment.mAttachmentPt;
-		if (attachment.mAdd) 
-			attachment_pt |= ATTACHMENT_ADD;
-
-		msg->nextBlockFast(_PREHASH_ObjectData );
-		msg->addUUIDFast(_PREHASH_ItemID, item->getLinkedUUID());
-		msg->addUUIDFast(_PREHASH_OwnerID, item->getPermissions().getOwner());
-		msg->addU8Fast(_PREHASH_AttachmentPt, attachment_pt);
-		pack_permissions_slam(msg, item->getFlags(), item->getPermissions());
-		msg->addStringFast(_PREHASH_Name, item->getName());
-		msg->addStringFast(_PREHASH_Description, item->getDescription());
 
 		if( (i+1 == obj_count) || ((OBJECTS_PER_PACKET-1) == (i % OBJECTS_PER_PACKET)) )
 		{
 			// End of message chunk
 			msg->sendReliable( gAgent.getRegion()->getHost() );
 		}
-		i++;
+        attachment_requests.pop_front();
 	}
+}
+
+void LLAttachmentsMgr::linkRecentlyArrivedAttachments()
+{
+    if (mRecentlyArrivedAttachments.size())
+    {
+        // One or more attachments have arrived but have not yet been
+        // processed for COF links
+        if (mAttachmentRequests.empty())
+        {
+            // Not waiting for any more.
+            LL_DEBUGS("Avatar") << "ATT all pending attachments have arrived after "
+                                << mCOFLinkBatchTimer.getElapsedTimeF32() << " seconds" << LL_ENDL;
+        }
+        else if (mCOFLinkBatchTimer.getElapsedTimeF32() > COF_LINK_BATCH_TIME)
+        {
+            LL_DEBUGS("Avatar") << "ATT " << mAttachmentRequests.size()
+                                << " pending attachments have not arrived, but wait time exceeded" << LL_ENDL;
+        }
+        else
+        {
+            return;
+        }
+
+        LL_DEBUGS("Avatar") << "ATT checking COF linkability for " << mRecentlyArrivedAttachments.size()
+                            << " recently arrived items" << LL_ENDL;
+
+        uuid_vec_t ids_to_link;
+        for (std::set<LLUUID>::iterator it = mRecentlyArrivedAttachments.begin();
+             it != mRecentlyArrivedAttachments.end(); ++it)
+        {
+            if (isAgentAvatarValid() &&
+                gAgentAvatarp->isWearingAttachment(*it) &&
+                !LLAppearanceMgr::instance().isLinkedInCOF(*it))
+            {
+                LLUUID item_id = *it;
+                LLViewerInventoryItem *item = gInventory.getItem(item_id);
+                LL_DEBUGS("Avatar") << "ATT adding COF link for attachment "
+                                    << (item ? item->getName() : "UNKNOWN") << " " << item_id << LL_ENDL;
+                ids_to_link.push_back(item_id);
+            }
+        }
+        if (ids_to_link.size())
+        {
+            LLPointer<LLInventoryCallback> cb = new LLRequestServerAppearanceUpdateOnDestroy();
+            for (uuid_vec_t::const_iterator uuid_it = ids_to_link.begin();
+                 uuid_it != ids_to_link.end(); ++uuid_it)
+            {
+                LLAppearanceMgr::instance().addCOFItemLink(*uuid_it, cb);
+            }
+        }
+        mRecentlyArrivedAttachments.clear();
+    }
+}
+
+LLAttachmentsMgr::LLItemRequestTimes::LLItemRequestTimes(const std::string& op_name, F32 timeout):
+    mOpName(op_name),
+    mTimeout(timeout)
+{
+}
+
+void LLAttachmentsMgr::LLItemRequestTimes::addTime(const LLUUID& inv_item_id)
+{
+    LLInventoryItem *item = gInventory.getItem(inv_item_id);
+    LL_DEBUGS("Avatar") << "ATT " << mOpName << " adding request time " << (item ? item->getName() : "UNKNOWN") << " " << inv_item_id << LL_ENDL;
+	LLTimer current_time;
+	(*this)[inv_item_id] = current_time;
+}
+
+void LLAttachmentsMgr::LLItemRequestTimes::removeTime(const LLUUID& inv_item_id)
+{
+    LLInventoryItem *item = gInventory.getItem(inv_item_id);
+	S32 remove_count = (*this).erase(inv_item_id);
+    if (remove_count)
+    {
+        LL_DEBUGS("Avatar") << "ATT " << mOpName << " removing request time "
+                            << (item ? item->getName() : "UNKNOWN") << " " << inv_item_id << LL_ENDL;
+    }
+}
+
+BOOL LLAttachmentsMgr::LLItemRequestTimes::getTime(const LLUUID& inv_item_id, LLTimer& timer) const
+{
+	std::map<LLUUID,LLTimer>::const_iterator it = (*this).find(inv_item_id);
+	if (it != (*this).end())
+	{
+        timer = it->second;
+        return TRUE;
+    }
+    return FALSE;
+}
+
+BOOL LLAttachmentsMgr::LLItemRequestTimes::wasRequestedRecently(const LLUUID& inv_item_id) const
+{
+    LLTimer request_time;
+    if (getTime(inv_item_id, request_time))
+    {
+		F32 request_time_elapsed = request_time.getElapsedTimeF32();
+        return request_time_elapsed < mTimeout;
+    }
+    else
+    {
+        return FALSE;
+    }
+}
+
+// If we've been waiting for an attachment a long time, we want to
+// forget the request, because if the request is invalid (say the
+// object does not exist), the existence of a request that never goes
+// away will gum up the COF batch logic, causing it to always wait for
+// the timeout. Expiring a request means if the item does show up
+// late, the COF link request may not get properly batched up, but
+// behavior will be no worse than before we had the batching mechanism
+// in place; the COF link will still be created, but extra
+// requestServerAppearanceUpdate() calls may occur.
+void LLAttachmentsMgr::expireOldAttachmentRequests()
+{
+	for (std::map<LLUUID,LLTimer>::iterator it = mAttachmentRequests.begin();
+         it != mAttachmentRequests.end(); )
+    {
+        std::map<LLUUID,LLTimer>::iterator curr_it = it;
+        ++it;
+        if (curr_it->second.getElapsedTimeF32() > MAX_ATTACHMENT_REQUEST_LIFETIME)
+        {
+            LLInventoryItem *item = gInventory.getItem(curr_it->first);
+            LL_WARNS("Avatar") << "ATT expiring request for attachment "
+                                << (item ? item->getName() : "UNKNOWN") << " item_id " << curr_it->first
+                                << " after " << MAX_ATTACHMENT_REQUEST_LIFETIME << " seconds" << LL_ENDL;
+            mAttachmentRequests.erase(curr_it);
+        }
+    }
+}
+
+void LLAttachmentsMgr::expireOldDetachRequests()
+{
+	for (std::map<LLUUID,LLTimer>::iterator it = mDetachRequests.begin();
+         it != mDetachRequests.end(); )
+    {
+        std::map<LLUUID,LLTimer>::iterator curr_it = it;
+        ++it;
+        if (curr_it->second.getElapsedTimeF32() > MAX_ATTACHMENT_REQUEST_LIFETIME)
+        {
+            LLInventoryItem *item = gInventory.getItem(curr_it->first);
+            LL_WARNS("Avatar") << "ATT expiring request for detach "
+                                << (item ? item->getName() : "UNKNOWN") << " item_id " << curr_it->first
+                                << " after " << MAX_ATTACHMENT_REQUEST_LIFETIME << " seconds" << LL_ENDL;
+            mDetachRequests.erase(curr_it);
+        }
+    }
+}
+
+// When an attachment arrives, we want to stop waiting for it, and add
+// it to the set of recently arrived items.
+void LLAttachmentsMgr::onAttachmentArrived(const LLUUID& inv_item_id)
+{
+    LLTimer timer;
+    bool expected = mAttachmentRequests.getTime(inv_item_id, timer);
+    if (!expected)
+    {
+        LLInventoryItem *item = gInventory.getItem(inv_item_id);
+        LL_WARNS() << "ATT Attachment was unexpected or arrived after " << MAX_ATTACHMENT_REQUEST_LIFETIME << " seconds: "
+                   << (item ? item->getName() : "UNKNOWN") << " id " << inv_item_id << LL_ENDL;
+    }
+    mAttachmentRequests.removeTime(inv_item_id);
+    if (expected && mAttachmentRequests.empty())
+    {
+        // mAttachmentRequests just emptied out
+        LL_DEBUGS("Avatar") << "ATT all active attachment requests have completed" << LL_ENDL;
+    }
+    if (mRecentlyArrivedAttachments.empty())
+    {
+        // Start the timer for sending off a COF link batch.
+        mCOFLinkBatchTimer.reset();
+    }
+    mRecentlyArrivedAttachments.insert(inv_item_id);
+}
+
+void LLAttachmentsMgr::onDetachRequested(const LLUUID& inv_item_id)
+{
+    mDetachRequests.addTime(inv_item_id);
+}
+
+void LLAttachmentsMgr::onDetachCompleted(const LLUUID& inv_item_id)
+{
+    LLTimer timer;
+    LLInventoryItem *item = gInventory.getItem(inv_item_id);
+    if (mDetachRequests.getTime(inv_item_id, timer))
+    {
+        LL_DEBUGS("Avatar") << "ATT detach completed after " << timer.getElapsedTimeF32()
+                            << " seconds for " << (item ? item->getName() : "UNKNOWN") << " " << inv_item_id << LL_ENDL;
+        mDetachRequests.removeTime(inv_item_id);
+        if (mDetachRequests.empty())
+        {
+            LL_DEBUGS("Avatar") << "ATT all detach requests have completed" << LL_ENDL;
+        }
+    }
+    else
+    {
+        LL_WARNS() << "ATT unexpected detach for "
+                   << (item ? item->getName() : "UNKNOWN") << " id " << inv_item_id << LL_ENDL;
+    }
+
+    LL_DEBUGS("Avatar") << "ATT detached item flagging as questionable for COF link checking "
+                        << (item ? item->getName() : "UNKNOWN") << " id " << inv_item_id << LL_ENDL;
+    mQuestionableCOFLinks.addTime(inv_item_id);
+}
+
+// Check for attachments that are (a) linked in COF and (b) not
+// attached to the avatar.  This is a rotten function to have to
+// include, because it runs the risk of either repeatedly spamming out
+// COF link removals if they're failing for some reason, or getting
+// into a tug of war with some other sequence of events that's in the
+// process of adding the attachment in question. However, it's needed
+// because we have no definitive source of authority for what things
+// are actually supposed to be attached. Scripts, run on the server
+// side, can remove an attachment without our expecting it. If this
+// happens to an attachment that's just been added, then the COF link
+// creation may still be in flight, and we will have to delete the
+// link after it shows up.
+//
+// Note that we only flag items for possible link removal if they have
+// been previously detached. This means that an attachment failure
+// will leave the link in the COF, where it will hopefully resolve
+// correctly on relog.
+//
+// See related: MAINT-5070, MAINT-4409
+//
+void LLAttachmentsMgr::checkInvalidCOFLinks()
+{
+        LLInventoryModel::cat_array_t cat_array;
+        LLInventoryModel::item_array_t item_array;
+        gInventory.collectDescendents(LLAppearanceMgr::instance().getCOF(),
+                                      cat_array,item_array,LLInventoryModel::EXCLUDE_TRASH);
+        for (S32 i=0; i<item_array.size(); i++)
+        {
+            const LLViewerInventoryItem* inv_item = item_array.at(i).get();
+            const LLUUID& item_id = inv_item->getLinkedUUID();
+            if (inv_item->getType() == LLAssetType::AT_OBJECT)
+            {
+                LLTimer timer;
+                bool is_flagged_questionable = mQuestionableCOFLinks.getTime(item_id,timer);
+                bool is_wearing_attachment = isAgentAvatarValid() && gAgentAvatarp->isWearingAttachment(item_id);
+                if (is_wearing_attachment && is_flagged_questionable)
+                {
+                    LL_DEBUGS("Avatar") << "ATT was flagged questionable but is now " 
+                                        << (is_wearing_attachment ? "attached " : "") 
+                                        <<"removing flag after "
+                                        << timer.getElapsedTimeF32() << " item "
+                                        << inv_item->getName() << " id " << item_id << LL_ENDL;
+                    mQuestionableCOFLinks.removeTime(item_id);
+                }
+            }
+        }
+
+        for(LLItemRequestTimes::iterator it = mQuestionableCOFLinks.begin();
+            it != mQuestionableCOFLinks.end(); )
+        {
+            LLItemRequestTimes::iterator curr_it = it;
+            ++it;
+            const LLUUID& item_id = curr_it->first;
+            LLViewerInventoryItem *inv_item = gInventory.getItem(item_id);
+            if (curr_it->second.getElapsedTimeF32() > MAX_BAD_COF_TIME)
+            {
+                if (LLAppearanceMgr::instance().isLinkedInCOF(item_id))
+                {
+                    LL_DEBUGS("Avatar") << "ATT Linked in COF but not attached or requested, deleting link after "
+                                        << curr_it->second.getElapsedTimeF32() << " seconds for " 
+                                        << (inv_item ? inv_item->getName() : "UNKNOWN") << " id " << item_id << LL_ENDL;
+                    LLAppearanceMgr::instance().removeCOFItemLinks(item_id);
+                }
+				mQuestionableCOFLinks.erase(curr_it);
+                continue;
+            }
+        }
+}
+
+void LLAttachmentsMgr::spamStatusInfo()
+{
+#if 0
+    static LLTimer spam_timer;
+    const F32 spam_frequency = 100.0F;
 
-	mPendingAttachments.clear();
+    if (spam_timer.getElapsedTimeF32() > spam_frequency)
+    {
+        spam_timer.reset();
+        
+        LLInventoryModel::cat_array_t cat_array;
+        LLInventoryModel::item_array_t item_array;
+        gInventory.collectDescendents(LLAppearanceMgr::instance().getCOF(),
+                                      cat_array,item_array,LLInventoryModel::EXCLUDE_TRASH);
+        for (S32 i=0; i<item_array.size(); i++)
+        {
+            const LLViewerInventoryItem* inv_item = item_array.at(i).get();
+            if (inv_item->getType() == LLAssetType::AT_OBJECT)
+            {
+                LL_DEBUGS("Avatar") << "item_id: " << inv_item->getUUID()
+                                    << " linked_item_id: " << inv_item->getLinkedUUID()
+                                    << " name: " << inv_item->getName()
+                                    << " parent: " << inv_item->getParentUUID()
+                                    << LL_ENDL;
+            }
+        }
+    }
+#endif
 }
diff --git a/indra/newview/llattachmentsmgr.h b/indra/newview/llattachmentsmgr.h
index 1d8ab74dfd9b6caa739f60b9bcb7d2ece67947d8..d56d6eb27bc2055c36fbf517f0765ba7d0233f6a 100755
--- a/indra/newview/llattachmentsmgr.h
+++ b/indra/newview/llattachmentsmgr.h
@@ -32,42 +32,101 @@
 
 class LLViewerInventoryItem;
 
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//--------------------------------------------------------------------------------
 // LLAttachmentsMgr
 // 
-// The sole purpose of this class is to take attachment
-// requests, queue them up, and send them all at once.
-// This handles situations where the viewer may request
-// a bunch of attachments at once in a short period of
-// time, where each of the requests would normally be
-// sent as a separate message versus being batched into
-// one single message.
-// 
-// The intent of this batching is to reduce viewer->server
-// traffic.
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// This class manages batching up of requests at two stages of
+// attachment rezzing.
+//
+// First, attachments requested to rez get saved in
+// mPendingAttachments and sent as a single
+// RezMultipleAttachmentsFromInv request. This batching is needed
+// mainly because of weaknessing the UI element->inventory item
+// handling, such that we don't always know when we are requesting
+// multiple items. Now they just pile up and get swept into a single
+// request during the idle loop.
+//
+// Second, after attachments arrive, we need to generate COF links for
+// them. There are both efficiency and UI correctness reasons why it
+// is better to request all the COF links at once and run a single
+// callback after they all complete. Given the vagaries of the
+// attachment system, there is no guarantee that we will get all the
+// attachments we ask for, but we frequently do. So in the common case
+// that all the desired attachments arrive fairly quickly, we generate
+// a single batched request for COF links. If attachments arrive late
+// or not at all, we will still issue COF link requests once a timeout
+// value has been exceeded.
+//
+// To handle attachments that never arrive, we forget about requests
+// that exceed a timeout value.
+//--------------------------------------------------------------------------------
 class LLAttachmentsMgr: public LLSingleton<LLAttachmentsMgr>
 {
 public:
-	LLAttachmentsMgr();
-	virtual ~LLAttachmentsMgr();
-
-	void addAttachment(const LLUUID& item_id,
-					   const U8 attachment_pt,
-					   const BOOL add);
-	static void onIdle(void *);
-protected:
-	void onIdle();
-private:
+    // Stores info for attachments that will be requested during idle.
 	struct AttachmentsInfo
 	{
 		LLUUID mItemID;
 		U8 mAttachmentPt;
 		BOOL mAdd;
 	};
+	typedef std::deque<AttachmentsInfo> attachments_vec_t;
+
+	LLAttachmentsMgr();
+	virtual ~LLAttachmentsMgr();
+
+	void addAttachmentRequest(const LLUUID& item_id,
+                              const U8 attachment_pt,
+                              const BOOL add);
+    void onAttachmentRequested(const LLUUID& item_id);
+	void requestAttachments(attachments_vec_t& attachment_requests);
+	static void onIdle(void *);
 
-	typedef std::vector<AttachmentsInfo> attachments_vec_t;
+    void onAttachmentArrived(const LLUUID& inv_item_id);
+
+    void onDetachRequested(const LLUUID& inv_item_id);
+    void onDetachCompleted(const LLUUID& inv_item_id);
+
+private:
+
+    class LLItemRequestTimes: public std::map<LLUUID,LLTimer>
+    {
+    public:
+        LLItemRequestTimes(const std::string& op_name, F32 timeout);
+        void addTime(const LLUUID& inv_item_id);
+        void removeTime(const LLUUID& inv_item_id);
+        BOOL wasRequestedRecently(const LLUUID& item_id) const;
+        BOOL getTime(const LLUUID& inv_item_id, LLTimer& timer) const;
+
+    private:
+        F32 mTimeout;
+        std::string mOpName;
+    };
+
+	void removeAttachmentRequestTime(const LLUUID& inv_item_id);
+	void onIdle();
+	void requestPendingAttachments();
+	void linkRecentlyArrivedAttachments();
+    void expireOldAttachmentRequests();
+    void expireOldDetachRequests();
+    void checkInvalidCOFLinks();
+    void spamStatusInfo();
+
+    // Attachments that we are planning to rez but haven't requested from the server yet.
 	attachments_vec_t mPendingAttachments;
+
+	// Attachments that have been requested from server but have not arrived yet.
+	LLItemRequestTimes mAttachmentRequests;
+
+    // Attachments that have been requested to detach but have not gone away yet.
+	LLItemRequestTimes mDetachRequests;
+
+    // Attachments that have arrived but have not been linked in the COF yet.
+    std::set<LLUUID> mRecentlyArrivedAttachments;
+    LLTimer mCOFLinkBatchTimer;
+
+    // Attachments that are linked in the COF but may be invalid.
+	LLItemRequestTimes mQuestionableCOFLinks;
 };
 
 #endif
diff --git a/indra/newview/llgesturemgr.cpp b/indra/newview/llgesturemgr.cpp
index 119872ec29eabad74ad0c39429827c16cc1e9080..950a6cfaef5b053b630bd70f9126baf8cfdcde4f 100755
--- a/indra/newview/llgesturemgr.cpp
+++ b/indra/newview/llgesturemgr.cpp
@@ -59,10 +59,6 @@
 // Longest time, in seconds, to wait for all animations to stop playing
 const F32 MAX_WAIT_ANIM_SECS = 30.f;
 
-// If this gesture is a link, get the base gesture that this link points to,
-// otherwise just return this id.
-static const LLUUID& get_linked_uuid(const LLUUID& item_id);
-
 // Lightweight constructor.
 // init() does the heavy lifting.
 LLGestureMgr::LLGestureMgr()
@@ -253,7 +249,7 @@ void LLGestureMgr::activateGestureWithAsset(const LLUUID& item_id,
 												BOOL inform_server,
 												BOOL deactivate_similar)
 {
-	const LLUUID& base_item_id = get_linked_uuid(item_id);
+	const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id);
 
 	if( !gAssetStorage )
 	{
@@ -307,7 +303,7 @@ void notify_update_label(const LLUUID& base_item_id)
 
 void LLGestureMgr::deactivateGesture(const LLUUID& item_id)
 {
-	const LLUUID& base_item_id = get_linked_uuid(item_id);
+	const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id);
 	item_map_t::iterator it = mActive.find(base_item_id);
 	if (it == mActive.end())
 	{
@@ -353,7 +349,7 @@ void LLGestureMgr::deactivateGesture(const LLUUID& item_id)
 
 void LLGestureMgr::deactivateSimilarGestures(LLMultiGesture* in, const LLUUID& in_item_id)
 {
-	const LLUUID& base_in_item_id = get_linked_uuid(in_item_id);
+	const LLUUID& base_in_item_id = gInventory.getLinkedItemID(in_item_id);
 	uuid_vec_t gest_item_ids;
 
 	// Deactivate all gestures that match
@@ -440,7 +436,7 @@ void LLGestureMgr::deactivateSimilarGestures(LLMultiGesture* in, const LLUUID& i
 
 BOOL LLGestureMgr::isGestureActive(const LLUUID& item_id)
 {
-	const LLUUID& base_item_id = get_linked_uuid(item_id);
+	const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id);
 	item_map_t::iterator it = mActive.find(base_item_id);
 	return (it != mActive.end());
 }
@@ -448,7 +444,7 @@ BOOL LLGestureMgr::isGestureActive(const LLUUID& item_id)
 
 BOOL LLGestureMgr::isGesturePlaying(const LLUUID& item_id)
 {
-	const LLUUID& base_item_id = get_linked_uuid(item_id);
+	const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id);
 
 	item_map_t::iterator it = mActive.find(base_item_id);
 	if (it == mActive.end()) return FALSE;
@@ -471,7 +467,7 @@ BOOL LLGestureMgr::isGesturePlaying(LLMultiGesture* gesture)
 
 void LLGestureMgr::replaceGesture(const LLUUID& item_id, LLMultiGesture* new_gesture, const LLUUID& asset_id)
 {
-	const LLUUID& base_item_id = get_linked_uuid(item_id);
+	const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id);
 
 	item_map_t::iterator it = mActive.find(base_item_id);
 	if (it == mActive.end())
@@ -513,7 +509,7 @@ void LLGestureMgr::replaceGesture(const LLUUID& item_id, LLMultiGesture* new_ges
 
 void LLGestureMgr::replaceGesture(const LLUUID& item_id, const LLUUID& new_asset_id)
 {
-	const LLUUID& base_item_id = get_linked_uuid(item_id);
+	const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id);
 
 	item_map_t::iterator it = LLGestureMgr::instance().mActive.find(base_item_id);
 	if (it == mActive.end())
@@ -608,7 +604,7 @@ void LLGestureMgr::playGesture(LLMultiGesture* gesture)
 // Convenience function that looks up the item_id for you.
 void LLGestureMgr::playGesture(const LLUUID& item_id)
 {
-	const LLUUID& base_item_id = get_linked_uuid(item_id);
+	const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id);
 
 	item_map_t::iterator it = mActive.find(base_item_id);
 	if (it == mActive.end()) return;
@@ -1297,7 +1293,7 @@ void LLGestureMgr::stopGesture(LLMultiGesture* gesture)
 
 void LLGestureMgr::stopGesture(const LLUUID& item_id)
 {
-	const LLUUID& base_item_id = get_linked_uuid(item_id);
+	const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id);
 
 	item_map_t::iterator it = mActive.find(base_item_id);
 	if (it == mActive.end()) return;
@@ -1457,14 +1453,4 @@ void LLGestureMgr::done()
 	}
 }
 
-// static
-const LLUUID& get_linked_uuid(const LLUUID &item_id)
-{
-	LLViewerInventoryItem* item = gInventory.getItem(item_id);
-	if (item && item->getIsLinkType())
-	{
-		return item->getLinkedUUID();
-	}
-	return item_id;
-}
 
diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp
index 31bca6b9a9a4d1b3a261af20c05810a38e8a5041..63e91f5d888a5ff69d4efdf64d108b32b3801d7c 100755
--- a/indra/newview/llinventorybridge.cpp
+++ b/indra/newview/llinventorybridge.cpp
@@ -5442,13 +5442,11 @@ void rez_attachment(LLViewerInventoryItem* item, LLViewerJointAttachment* attach
 
 	// Check for duplicate request.
 	if (isAgentAvatarValid() &&
-		(gAgentAvatarp->attachmentWasRequested(item_id) ||
-		 gAgentAvatarp->isWearingAttachment(item_id)))
+		gAgentAvatarp->isWearingAttachment(item_id))
 	{
-		LL_WARNS() << "duplicate attachment request, ignoring" << LL_ENDL;
+		LL_WARNS() << "ATT duplicate attachment request, ignoring" << LL_ENDL;
 		return;
 	}
-	gAgentAvatarp->addAttachmentRequest(item_id);
 
 	S32 attach_pt = 0;
 	if (isAgentAvatarValid() && attachment)
@@ -5498,36 +5496,14 @@ bool confirm_attachment_rez(const LLSD& notification, const LLSD& response)
 
 		if (itemp)
 		{
-			/*
-			{
-				U8 attachment_pt = notification["payload"]["attachment_point"].asInteger();
-				
-				LLMessageSystem* msg = gMessageSystem;
-				msg->newMessageFast(_PREHASH_RezSingleAttachmentFromInv);
-				msg->nextBlockFast(_PREHASH_AgentData);
-				msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
-				msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
-				msg->nextBlockFast(_PREHASH_ObjectData);
-				msg->addUUIDFast(_PREHASH_ItemID, itemp->getUUID());
-				msg->addUUIDFast(_PREHASH_OwnerID, itemp->getPermissions().getOwner());
-				msg->addU8Fast(_PREHASH_AttachmentPt, attachment_pt);
-				pack_permissions_slam(msg, itemp->getFlags(), itemp->getPermissions());
-				msg->addStringFast(_PREHASH_Name, itemp->getName());
-				msg->addStringFast(_PREHASH_Description, itemp->getDescription());
-				msg->sendReliable(gAgent.getRegion()->getHost());
-				return false;
-			}
-			*/
-
 			// Queue up attachments to be sent in next idle tick, this way the
 			// attachments are batched up all into one message versus each attachment
 			// being sent in its own separate attachments message.
 			U8 attachment_pt = notification["payload"]["attachment_point"].asInteger();
 			BOOL is_add = notification["payload"]["is_add"].asBoolean();
 
-			LLAttachmentsMgr::instance().addAttachment(item_id,
-													   attachment_pt,
-													   is_add);
+			LL_DEBUGS("Avatar") << "ATT calling addAttachmentRequest " << (itemp ? itemp->getName() : "UNKNOWN") << " id " << item_id << LL_ENDL;
+			LLAttachmentsMgr::instance().addAttachmentRequest(item_id, attachment_pt, is_add);
 		}
 	}
 	return false;
diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h
index 61c86ff05ee2efec57e0ab359c2bef2bd8e75384..f6fea5df7cb0ca0c6a016486542df82432d98ab2 100755
--- a/indra/newview/llinventorybridge.h
+++ b/indra/newview/llinventorybridge.h
@@ -690,6 +690,12 @@ void hide_context_entries(LLMenuGL& menu,
 						  const menuentry_vec_t &entries_to_show, 
 						  const menuentry_vec_t &disabled_entries);
 
+// Helper functions to classify actions.
+bool isAddAction(const std::string& action);
+bool isRemoveAction(const std::string& action);
+bool isMarketplaceCopyAction(const std::string& action);
+bool isMarketplaceSendAction(const std::string& action);
+
 class LLFolderViewGroupedItemBridge: public LLFolderViewGroupedItemModel
 {
 public:
diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp
index 58cab8a058d236a3151887baa8c2722f3da08e04..ae799e8b18d101d1f79d39e81510027e4904b68b 100755
--- a/indra/newview/llinventoryfunctions.cpp
+++ b/indra/newview/llinventoryfunctions.cpp
@@ -231,7 +231,7 @@ BOOL get_is_item_worn(const LLUUID& id)
 		return FALSE;
 
 	// Consider the item as worn if it has links in COF.
-	if (LLAppearanceMgr::instance().isLinkInCOF(id))
+	if (LLAppearanceMgr::instance().isLinkedInCOF(id))
 	{
 		return TRUE;
 	}
@@ -265,7 +265,7 @@ BOOL get_can_item_be_worn(const LLUUID& id)
 	if (!item)
 		return FALSE;
 
-	if (LLAppearanceMgr::isLinkInCOF(item->getLinkedUUID()))
+	if (LLAppearanceMgr::instance().isLinkedInCOF(item->getLinkedUUID()))
 	{
 		// an item having links in COF (i.e. a worn item)
 		return FALSE;
@@ -1074,6 +1074,34 @@ void LLOpenFoldersWithSelection::doFolder(LLFolderViewFolder* folder)
 	}
 }
 
+// Succeeds iff all selected items are bridges to objects, in which
+// case returns their corresponding uuids.
+bool get_selection_object_uuids(LLFolderView *root, uuid_vec_t& ids)
+{
+	uuid_vec_t results;
+	S32 non_object = 0;
+	LLFolderView::selected_items_t selectedItems = root->getSelectedItems();
+	for(LLFolderView::selected_items_t::iterator it = selectedItems.begin(); it != selectedItems.end(); ++it)
+	{
+		LLObjectBridge *view_model = dynamic_cast<LLObjectBridge *>((*it)->getViewModelItem());
+
+		if(view_model && view_model->getUUID().notNull())
+		{
+			results.push_back(view_model->getUUID());
+		}
+		else
+		{
+			non_object++;
+		}
+	}
+	if (non_object == 0)
+	{
+		ids = results;
+		return true;
+	}
+	return false;
+}
+
 void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root, const std::string& action)
 {
 	if ("rename" == action)
@@ -1128,7 +1156,6 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root
 		LLFloater::setFloaterHost(multi_propertiesp);
 	}
 
-    
 	std::set<LLUUID> selected_uuid_set = LLAvatarActions::getInventorySelectedUUIDs();
     uuid_vec_t ids;
     std::copy(selected_uuid_set.begin(), selected_uuid_set.end(), std::back_inserter(ids));
@@ -1141,7 +1168,7 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root
     {
         wear_multiple(ids, false);
     }
-    else if (action == "take_off" || action == "detach")
+    else if (isRemoveAction(action))
     {
         LLAppearanceMgr::instance().removeItemsFromAvatar(ids);
     }
diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp
index 2a107deb1987b2cb891f3a032b71efd64a48e468..ad86995b428c5bf8544808117cf4eb9ff0b8d031 100755
--- a/indra/newview/llselectmgr.cpp
+++ b/indra/newview/llselectmgr.cpp
@@ -52,6 +52,7 @@
 // viewer includes
 #include "llagent.h"
 #include "llagentcamera.h"
+#include "llattachmentsmgr.h"
 #include "llviewerwindow.h"
 #include "lldrawable.h"
 #include "llfloaterinspect.h"
@@ -2374,6 +2375,7 @@ void LLSelectMgr::selectionSetIncludeInSearch(bool include_in_search)
 		"ObjectIncludeInSearch",
 		packAgentAndSessionID,
 		packObjectIncludeInSearch, 
+        logNoOp,
 		&include_in_search,
 		SEND_ONLY_ROOTS);
 }
@@ -2423,6 +2425,7 @@ void LLSelectMgr::selectionSetClickAction(U8 action)
 	sendListToRegions("ObjectClickAction",
 					  packAgentAndSessionID,
 					  packObjectClickAction, 
+                      logNoOp,
 					  &action,
 					  SEND_INDIVIDUALS);
 }
@@ -2458,7 +2461,7 @@ void LLSelectMgr::sendGodlikeRequest(const std::string& request, const std::stri
 	}
 	else
 	{
-		sendListToRegions(message_type, packGodlikeHead, packObjectIDAsParam, &data, SEND_ONLY_ROOTS);
+		sendListToRegions(message_type, packGodlikeHead, packObjectIDAsParam, logNoOp, &data, SEND_ONLY_ROOTS);
 	}
 }
 
@@ -2486,6 +2489,23 @@ void LLSelectMgr::packGodlikeHead(void* user_data)
 	}
 }
 
+// static
+void LLSelectMgr::logNoOp(LLSelectNode* node, void *)
+{
+}
+
+// static
+void LLSelectMgr::logAttachmentRequest(LLSelectNode* node, void *)
+{
+    LLAttachmentsMgr::instance().onAttachmentRequested(node->mItemID);
+}
+
+// static
+void LLSelectMgr::logDetachRequest(LLSelectNode* node, void *)
+{
+    LLAttachmentsMgr::instance().onDetachRequested(node->mItemID);
+}
+
 // static
 void LLSelectMgr::packObjectIDAsParam(LLSelectNode* node, void *)
 {
@@ -3592,10 +3612,11 @@ bool LLSelectMgr::confirmDelete(const LLSD& notification, const LLSD& response,
 			// attempt to derez into the trash.
 			LLDeRezInfo info(DRD_TRASH, trash_id);
 			LLSelectMgr::getInstance()->sendListToRegions("DeRezObject",
-										  packDeRezHeader,
-										  packObjectLocalID,
-										  (void*) &info,
-										  SEND_ONLY_ROOTS);
+                                                          packDeRezHeader,
+                                                          packObjectLocalID,
+                                                          logNoOp,
+                                                          (void*) &info,
+                                                          SEND_ONLY_ROOTS);
 			// VEFFECT: Delete Object - one effect for all deletes
 			if (LLSelectMgr::getInstance()->mSelectedObjects->mSelectType != SELECT_TYPE_HUD)
 			{
@@ -3627,6 +3648,7 @@ void LLSelectMgr::selectForceDelete()
 		"ObjectDelete",
 		packDeleteHeader,
 		packObjectLocalID,
+        logNoOp,
 		(void*)TRUE,
 		SEND_ONLY_ROOTS);
 }
@@ -3797,7 +3819,7 @@ void LLSelectMgr::selectDuplicate(const LLVector3& offset, BOOL select_copy)
 	data.offset = offset;
 	data.flags = (select_copy ? FLAGS_CREATE_SELECTED : 0x0);
 
-	sendListToRegions("ObjectDuplicate", packDuplicateHeader, packDuplicate, &data, SEND_ONLY_ROOTS);
+	sendListToRegions("ObjectDuplicate", packDuplicateHeader, packDuplicate, logNoOp, &data, SEND_ONLY_ROOTS);
 
 	if (select_copy)
 	{
@@ -3852,7 +3874,7 @@ void LLSelectMgr::repeatDuplicate()
 	data.offset = LLVector3::zero;
 	data.flags = 0x0;
 
-	sendListToRegions("ObjectDuplicate", packDuplicateHeader, packDuplicate, &data, SEND_ONLY_ROOTS);
+	sendListToRegions("ObjectDuplicate", packDuplicateHeader, packDuplicate, logNoOp, &data, SEND_ONLY_ROOTS);
 
 	// move current selection based on delta from duplication position and update duplication position
 	for (LLObjectSelection::root_iterator iter = getSelection()->root_begin();
@@ -3931,7 +3953,7 @@ void LLSelectMgr::selectDuplicateOnRay(const LLVector3 &ray_start_region,
 	data.mFlags				= (select_copy ? FLAGS_CREATE_SELECTED : 0x0);
 
 	sendListToRegions("ObjectDuplicateOnRay", 
-		packDuplicateOnRayHead, packObjectLocalID, &data, SEND_ONLY_ROOTS);
+                      packDuplicateOnRayHead, packObjectLocalID, logNoOp, &data, SEND_ONLY_ROOTS);
 
 	if (select_copy)
 	{
@@ -3981,6 +4003,7 @@ void LLSelectMgr::sendMultipleUpdate(U32 type)
 		"MultipleObjectUpdate",
 		packAgentAndSessionID,
 		packMultipleUpdate,
+        logNoOp,
 		&type,
 		send_type);
 }
@@ -4044,7 +4067,7 @@ void LLSelectMgr::sendOwner(const LLUUID& owner_id,
 	data.group_id = group_id;
 	data.override = override;
 
-	sendListToRegions("ObjectOwner", packOwnerHead, packObjectLocalID, &data, SEND_ONLY_ROOTS);
+	sendListToRegions("ObjectOwner", packOwnerHead, packObjectLocalID, logNoOp, &data, SEND_ONLY_ROOTS);
 }
 
 // static
@@ -4068,7 +4091,7 @@ void LLSelectMgr::packOwnerHead(void *user_data)
 void LLSelectMgr::sendGroup(const LLUUID& group_id)
 {
 	LLUUID local_group_id(group_id);
-	sendListToRegions("ObjectGroup", packAgentAndSessionAndGroupID, packObjectLocalID, &local_group_id, SEND_ONLY_ROOTS);
+	sendListToRegions("ObjectGroup", packAgentAndSessionAndGroupID, packObjectLocalID, logNoOp, &local_group_id, SEND_ONLY_ROOTS);
 }
 
 
@@ -4092,7 +4115,7 @@ void LLSelectMgr::sendBuy(const LLUUID& buyer_id, const LLUUID& category_id, con
 	LLBuyData buy;
 	buy.mCategoryID = category_id;
 	buy.mSaleInfo = sale_info;
-	sendListToRegions("ObjectBuy", packAgentGroupAndCatID, packBuyObjectIDs, &buy, SEND_ONLY_ROOTS);
+	sendListToRegions("ObjectBuy", packAgentGroupAndCatID, packBuyObjectIDs, logNoOp, &buy, SEND_ONLY_ROOTS);
 }
 
 // static
@@ -4136,7 +4159,7 @@ void LLSelectMgr::selectionSetObjectPermissions(U8 field,
 	data.mMask = mask;
 	data.mOverride = override;
 
-	sendListToRegions("ObjectPermissions", packPermissionsHead, packPermissions, &data, SEND_ONLY_ROOTS);
+	sendListToRegions("ObjectPermissions", packPermissionsHead, packPermissions, logNoOp, &data, SEND_ONLY_ROOTS);
 }
 
 void LLSelectMgr::packPermissionsHead(void* user_data)
@@ -4179,6 +4202,7 @@ void LLSelectMgr::deselectAll()
 		"ObjectDeselect",
 		packAgentAndSessionID,
 		packObjectLocalID,
+        logNoOp,
 		NULL,
 		SEND_INDIVIDUALS);
 
@@ -4209,6 +4233,7 @@ void LLSelectMgr::deselectAllForStandingUp()
 		"ObjectDeselect",
 		packAgentAndSessionID,
 		packObjectLocalID,
+        logNoOp,
 		NULL,
 		SEND_INDIVIDUALS);
 
@@ -4290,6 +4315,7 @@ void LLSelectMgr::selectionSetObjectName(const std::string& name)
 		sendListToRegions("ObjectName",
 						  packAgentAndSessionID,
 						  packObjectName,
+                          logNoOp,
 						  (void*)(&name_copy),
 						  SEND_ONLY_ROOTS);
 	}
@@ -4298,6 +4324,7 @@ void LLSelectMgr::selectionSetObjectName(const std::string& name)
 		sendListToRegions("ObjectName",
 						  packAgentAndSessionID,
 						  packObjectName,
+                          logNoOp,
 						  (void*)(&name_copy),
 						  SEND_INDIVIDUALS);
 	}
@@ -4313,6 +4340,7 @@ void LLSelectMgr::selectionSetObjectDescription(const std::string& desc)
 		sendListToRegions("ObjectDescription",
 						  packAgentAndSessionID,
 						  packObjectDescription,
+                          logNoOp,
 						  (void*)(&desc_copy),
 						  SEND_ONLY_ROOTS);
 	}
@@ -4321,6 +4349,7 @@ void LLSelectMgr::selectionSetObjectDescription(const std::string& desc)
 		sendListToRegions("ObjectDescription",
 						  packAgentAndSessionID,
 						  packObjectDescription,
+                          logNoOp,
 						  (void*)(&desc_copy),
 						  SEND_INDIVIDUALS);
 	}
@@ -4334,6 +4363,7 @@ void LLSelectMgr::selectionSetObjectCategory(const LLCategory& category)
 	sendListToRegions("ObjectCategory",
 					  packAgentAndSessionID,
 					  packObjectCategory,
+                      logNoOp,
 					  (void*)(&category),
 					  SEND_ONLY_ROOTS);
 }
@@ -4343,6 +4373,7 @@ void LLSelectMgr::selectionSetObjectSaleInfo(const LLSaleInfo& sale_info)
 	sendListToRegions("ObjectSaleInfo",
 					  packAgentAndSessionID,
 					  packObjectSaleInfo,
+                      logNoOp,
 					  (void*)(&sale_info),
 					  SEND_ONLY_ROOTS);
 }
@@ -4376,6 +4407,7 @@ void LLSelectMgr::sendAttach(U8 attachment_point, bool replace)
 			"ObjectAttach",
 			packAgentIDAndSessionAndAttachment, 
 			packObjectIDAndRotation, 
+            logAttachmentRequest,
 			&attachment_point, 
 			SEND_ONLY_ROOTS );
 		if (!build_mode)
@@ -4396,6 +4428,7 @@ void LLSelectMgr::sendDetach()
 		"ObjectDetach",
 		packAgentAndSessionID,
 		packObjectLocalID,
+        logDetachRequest,
 		NULL,
 		SEND_ONLY_ROOTS );
 }
@@ -4412,6 +4445,7 @@ void LLSelectMgr::sendDropAttachment()
 		"ObjectDrop",
 		packAgentAndSessionID,
 		packObjectLocalID,
+        logDetachRequest,
 		NULL,
 		SEND_ONLY_ROOTS);
 }
@@ -4431,6 +4465,7 @@ void LLSelectMgr::sendLink()
 		"ObjectLink",
 		packAgentAndSessionID,
 		packObjectLocalID,
+        logNoOp,
 		NULL,
 		SEND_ONLY_ROOTS);
 }
@@ -4468,6 +4503,7 @@ void LLSelectMgr::sendDelink()
 		"ObjectDelink",
 		packAgentAndSessionID,
 		packObjectLocalID,
+        logNoOp,
 		NULL,
 		SEND_INDIVIDUALS);
 }
@@ -4520,6 +4556,7 @@ void LLSelectMgr::sendSelect()
 		"ObjectSelect",
 		packAgentAndSessionID,
 		packObjectLocalID,
+        logNoOp,
 		NULL,
 		SEND_INDIVIDUALS);
 }
@@ -4916,6 +4953,7 @@ void LLSelectMgr::packPermissions(LLSelectNode* node, void *user_data)
 void LLSelectMgr::sendListToRegions(const std::string& message_name,
 									void (*pack_header)(void *user_data), 
 									void (*pack_body)(LLSelectNode* node, void *user_data), 
+                                    void (*log_func)(LLSelectNode* node, void *user_data), 
 									void *user_data,
 									ESendType send_type)
 {
@@ -5046,6 +5084,8 @@ void LLSelectMgr::sendListToRegions(const std::string& message_name,
 			}
 			// add another instance of the body of the data
 			(*pack_body)(node, user_data);
+            // do any related logging
+            (*log_func)(node, user_data);
 			++objects_sent;
 			++objects_in_this_packet;
 
@@ -6712,7 +6752,7 @@ void LLSelectMgr::undo()
 {
 	BOOL select_linked_set = !gSavedSettings.getBOOL("EditLinkedParts");
 	LLUUID group_id(gAgent.getGroupID());
-	sendListToRegions("Undo", packAgentAndSessionAndGroupID, packObjectID, &group_id, select_linked_set ? SEND_ONLY_ROOTS : SEND_CHILDREN_FIRST);
+	sendListToRegions("Undo", packAgentAndSessionAndGroupID, packObjectID, logNoOp, &group_id, select_linked_set ? SEND_ONLY_ROOTS : SEND_CHILDREN_FIRST);
 }
 
 //-----------------------------------------------------------------------------
@@ -6730,7 +6770,7 @@ void LLSelectMgr::redo()
 {
 	BOOL select_linked_set = !gSavedSettings.getBOOL("EditLinkedParts");
 	LLUUID group_id(gAgent.getGroupID());
-	sendListToRegions("Redo", packAgentAndSessionAndGroupID, packObjectID, &group_id, select_linked_set ? SEND_ONLY_ROOTS : SEND_CHILDREN_FIRST);
+	sendListToRegions("Redo", packAgentAndSessionAndGroupID, packObjectID, logNoOp, &group_id, select_linked_set ? SEND_ONLY_ROOTS : SEND_CHILDREN_FIRST);
 }
 
 //-----------------------------------------------------------------------------
diff --git a/indra/newview/llselectmgr.h b/indra/newview/llselectmgr.h
index 316e72b11cfc5d527918e62444b0b2b99f3a73ae..87d25e3a8c1f8360e9b70298d75c61dcdb437a59 100755
--- a/indra/newview/llselectmgr.h
+++ b/indra/newview/llselectmgr.h
@@ -761,6 +761,7 @@ class LLSelectMgr : public LLEditMenuHandler, public LLSingleton<LLSelectMgr>
 	void sendListToRegions(	const std::string& message_name,
 							void (*pack_header)(void *user_data), 
 							void (*pack_body)(LLSelectNode* node, void *user_data), 
+							void (*log_func)(LLSelectNode* node, void *user_data), 
 							void *user_data,
 							ESendType send_type);
 
@@ -796,6 +797,9 @@ class LLSelectMgr : public LLEditMenuHandler, public LLSingleton<LLSelectMgr>
 	static void packHingeHead(void *user_data);
 	static void packPermissionsHead(void* user_data);
 	static void packGodlikeHead(void* user_data);
+    static void logNoOp(LLSelectNode* node, void *user_data);
+    static void logAttachmentRequest(LLSelectNode* node, void *user_data);
+    static void logDetachRequest(LLSelectNode* node, void *user_data);
 	static bool confirmDelete(const LLSD& notification, const LLSD& response, LLObjectSelectionHandle handle);
 
 	// Get the first ID that matches test and whether or not all ids are identical in selected objects.
diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp
index d1121180821c4abe08aa75fa88bdcc3897d15dbb..a8634d1e936edd7c85950707007caf38750d7d9d 100755
--- a/indra/newview/llviewerinventory.cpp
+++ b/indra/newview/llviewerinventory.cpp
@@ -2306,5 +2306,3 @@ BOOL LLViewerInventoryItem::regenerateLink()
 	gInventory.notifyObservers();
 	return TRUE;
 }
-
-
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index 5c55683d84508a0595bfd9abbc638b68ab647f10..7282c67cf943d9893f5962cfe763cbc4007f5fbf 100755
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -6650,7 +6650,7 @@ class LLAttachmentDetachFromPoint : public view_listener_t
 				LLViewerObject *attached_object = (*iter);
 				ids_to_remove.push_back(attached_object->getAttachmentItemID());
 			}
-			}
+        }
 		if (!ids_to_remove.empty())
 		{
 			LLAppearanceMgr::instance().removeItemsFromAvatar(ids_to_remove);
diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp
index b8ed17f3824aca572f590f55728d4434e8575c96..c521b0037289237b0bed8739ec5b756b4e20b620 100755
--- a/indra/newview/llviewerobject.cpp
+++ b/indra/newview/llviewerobject.cpp
@@ -1958,6 +1958,11 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys,
 				
 				if (sent_parentp && (sent_parentp != this) && !sent_parentp->isDead())
 				{
+                    if (((LLViewerObject*)sent_parentp)->isAvatar())
+                    {
+                        //LL_DEBUGS("Avatar") << "ATT got object update for attachment " << LL_ENDL; 
+                    }
+                    
 					//
 					// We have a viewer object for the parent, and it's not dead.
 					// Do the actual reparenting here.
diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp
index 7c36b30dd10dc3e0a339c160d601991300f967e4..75732a1e1911f97de6f0598a3fade8855e733279 100755
--- a/indra/newview/llviewerobjectlist.cpp
+++ b/indra/newview/llviewerobjectlist.cpp
@@ -474,13 +474,13 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,
 			{
 				U32 flags = 0;
 				mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_UpdateFlags, flags, i);
-			
+                
 				if(flags & FLAGS_TEMPORARY_ON_REZ)
 				{
-				compressed_dp.unpackUUID(fullid, "ID");
-				compressed_dp.unpackU32(local_id, "LocalID");
-				compressed_dp.unpackU8(pcode, "PCode");
-			}
+                    compressed_dp.unpackUUID(fullid, "ID");
+                    compressed_dp.unpackU32(local_id, "LocalID");
+                    compressed_dp.unpackU8(pcode, "PCode");
+                }
 				else //send to object cache
 				{
 					regionp->cacheFullUpdate(compressed_dp, flags);
@@ -497,7 +497,7 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,
 								 gMessageSystem->getSenderPort());
 				if (fullid.isNull())
 				{
-					// LL_WARNS() << "update for unknown localid " << local_id << " host " << gMessageSystem->getSender() << ":" << gMessageSystem->getSenderPort() << LL_ENDL;
+					LL_DEBUGS() << "update for unknown localid " << local_id << " host " << gMessageSystem->getSender() << ":" << gMessageSystem->getSenderPort() << LL_ENDL;
 					mNumUnknownUpdates++;
 				}
 			}
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index 2a393933d98ae6524f850dbd8f769aeb533891d2..b97a1bde99108eb48501b11c283a598f760a8398 100755
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -725,7 +725,7 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id,
 	const BOOL needsSendToSim = false; // currently, this HUD effect doesn't need to pack and unpack data to do its job
 	mVoiceVisualizer = ( LLVoiceVisualizer *)LLHUDManager::getInstance()->createViewerEffect( LLHUDObject::LL_HUD_EFFECT_VOICE_VISUALIZER, needsSendToSim );
 
-	LL_DEBUGS("Avatar") << "LLVOAvatar Constructor (0x" << this << ") id:" << mID << LL_ENDL;
+	LL_DEBUGS("Avatar","Message") << "LLVOAvatar Constructor (0x" << this << ") id:" << mID << LL_ENDL;
 
 	mPelvisp = NULL;
 
@@ -5737,12 +5737,20 @@ BOOL LLVOAvatar::setParent(LLViewerObject* parent)
 void LLVOAvatar::addChild(LLViewerObject *childp)
 {
 	childp->extractAttachmentItemID(); // find the inventory item this object is associated with.
+	if (isSelf())
+	{
+	    const LLUUID& item_id = childp->getAttachmentItemID();
+		LLViewerInventoryItem *item = gInventory.getItem(item_id);
+		LL_DEBUGS("Avatar") << "ATT attachment child added " << (item ? item->getName() : "UNKNOWN") << " id " << item_id << LL_ENDL;
+
+	}
+
 	LLViewerObject::addChild(childp);
 	if (childp->mDrawable)
 	{
 		if (!attachObject(childp))
 		{
-			LL_WARNS() << "addChild() failed for " 
+			LL_WARNS() << "ATT addChild() failed for " 
 					<< childp->getID()
 					<< " item " << childp->getAttachmentItemID()
 					<< LL_ENDL;
@@ -5812,10 +5820,21 @@ LLViewerJointAttachment* LLVOAvatar::getTargetAttachmentPoint(LLViewerObject* vi
 //-----------------------------------------------------------------------------
 const LLViewerJointAttachment *LLVOAvatar::attachObject(LLViewerObject *viewer_object)
 {
+	if (isSelf())
+	{
+		const LLUUID& item_id = viewer_object->getAttachmentItemID();
+		LLViewerInventoryItem *item = gInventory.getItem(item_id);
+		LL_DEBUGS("Avatar") << "ATT attaching object "
+							<< (item ? item->getName() : "UNKNOWN") << " id " << item_id << LL_ENDL;	
+	}
 	LLViewerJointAttachment* attachment = getTargetAttachmentPoint(viewer_object);
 
 	if (!attachment || !attachment->addObject(viewer_object))
 	{
+		const LLUUID& item_id = viewer_object->getAttachmentItemID();
+		LLViewerInventoryItem *item = gInventory.getItem(item_id);
+		LL_WARNS("Avatar") << "ATT attach failed "
+						   << (item ? item->getName() : "UNKNOWN") << " id " << item_id << LL_ENDL;	
 		return 0;
 	}
 
@@ -5875,6 +5894,13 @@ void LLVOAvatar::lazyAttach()
 		LLPointer<LLViewerObject> cur_attachment = mPendingAttachment[i];
 		if (cur_attachment->mDrawable)
 		{
+			if (isSelf())
+			{
+				const LLUUID& item_id = cur_attachment->getAttachmentItemID();
+				LLViewerInventoryItem *item = gInventory.getItem(item_id);
+				LL_DEBUGS("Avatar") << "ATT attaching object "
+									<< (item ? item->getName() : "UNKNOWN") << " id " << item_id << LL_ENDL;
+			}
 			if (!attachObject(cur_attachment))
 			{	// Drop it
 				LL_WARNS() << "attachObject() failed for " 
diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp
index 56997c928ad9a20d11bc8e94112533e0b251b3c2..c1ca0aed69cd3ea754a85fa626726179b0a08901 100755
--- a/indra/newview/llvoavatarself.cpp
+++ b/indra/newview/llvoavatarself.cpp
@@ -38,6 +38,7 @@
 #include "pipeline.h"
 
 #include "llagent.h" //  Get state values from here
+#include "llattachmentsmgr.h"
 #include "llagentcamera.h"
 #include "llagentwearables.h"
 #include "llhudeffecttrail.h"
@@ -1117,44 +1118,6 @@ BOOL LLVOAvatarSelf::isWearingAttachment(const LLUUID& inv_item_id) const
 	return FALSE;
 }
 
-//-----------------------------------------------------------------------------
-BOOL LLVOAvatarSelf::attachmentWasRequested(const LLUUID& inv_item_id) const
-{
-	const F32 REQUEST_EXPIRATION_SECONDS = 5.0;  // any request older than this is ignored/removed.
-	std::map<LLUUID,LLTimer>::iterator it = mAttachmentRequests.find(inv_item_id);
-	if (it != mAttachmentRequests.end())
-	{
-		const LLTimer& request_time = it->second;
-		F32 request_time_elapsed = request_time.getElapsedTimeF32();
-		if (request_time_elapsed > REQUEST_EXPIRATION_SECONDS)
-		{
-			mAttachmentRequests.erase(it);
-			return FALSE;
-		}
-		else
-		{
-			return TRUE;
-		}
-	}
-	else
-	{
-		return FALSE;
-	}
-}
-
-//-----------------------------------------------------------------------------
-void LLVOAvatarSelf::addAttachmentRequest(const LLUUID& inv_item_id)
-{
-	LLTimer current_time;
-	mAttachmentRequests[inv_item_id] = current_time;
-}
-
-//-----------------------------------------------------------------------------
-void LLVOAvatarSelf::removeAttachmentRequest(const LLUUID& inv_item_id)
-{
-	mAttachmentRequests.erase(inv_item_id);
-}
-
 //-----------------------------------------------------------------------------
 // getWornAttachment()
 //-----------------------------------------------------------------------------
@@ -1221,8 +1184,6 @@ const LLViewerJointAttachment *LLVOAvatarSelf::attachObject(LLViewerObject *view
 	{
 		const LLUUID& attachment_id = viewer_object->getAttachmentItemID();
 		LLAppearanceMgr::instance().registerAttachment(attachment_id);
-		// Clear any pending requests once the attachment arrives.
-		removeAttachmentRequest(attachment_id);
 		updateLODRiggedAttachments();		
 	}
 
diff --git a/indra/newview/llvoavatarself.h b/indra/newview/llvoavatarself.h
index dc5e64d547888135f001e156bc49eeb290b7ad92..332a6353ad9f73a2adcaf48d8a3cfdf1ae9d93c0 100755
--- a/indra/newview/llvoavatarself.h
+++ b/indra/newview/llvoavatarself.h
@@ -291,19 +291,12 @@ class LLVOAvatarSelf :
 public:
 	void 				updateAttachmentVisibility(U32 camera_mode);
 	BOOL 				isWearingAttachment(const LLUUID& inv_item_id) const;
-	BOOL				attachmentWasRequested(const LLUUID& inv_item_id) const;
-	void				addAttachmentRequest(const LLUUID& inv_item_id);
-	void				removeAttachmentRequest(const LLUUID& inv_item_id);
 	LLViewerObject* 	getWornAttachment(const LLUUID& inv_item_id);
 	bool				getAttachedPointName(const LLUUID& inv_item_id, std::string& name) const;
 	/*virtual*/ const LLViewerJointAttachment *attachObject(LLViewerObject *viewer_object);
 	/*virtual*/ BOOL 	detachObject(LLViewerObject *viewer_object);
 	static BOOL			detachAttachmentIntoInventory(const LLUUID& item_id);
 
-private:
-	// Track attachments that have been requested but have not arrived yet.
-	mutable std::map<LLUUID,LLTimer> mAttachmentRequests;
-
 	//--------------------------------------------------------------------
 	// HUDs
 	//--------------------------------------------------------------------
diff --git a/indra/newview/llwearableitemslist.cpp b/indra/newview/llwearableitemslist.cpp
index 888ead06137043b19378ebafd11d097e255244a3..1c3808ce6879ce6498d1387a40098b9517f8f239 100755
--- a/indra/newview/llwearableitemslist.cpp
+++ b/indra/newview/llwearableitemslist.cpp
@@ -124,7 +124,7 @@ void LLPanelWearableOutfitItem::updateItem(const std::string& name,
 	// We don't use get_is_item_worn() here because this update is triggered by
 	// an inventory observer upon link in COF beind added or removed so actual
 	// worn status of a linked item may still remain unchanged.
-	if (mWornIndicationEnabled && LLAppearanceMgr::instance().isLinkInCOF(mInventoryItemUUID))
+	if (mWornIndicationEnabled && LLAppearanceMgr::instance().isLinkedInCOF(mInventoryItemUUID))
 	{
 		search_label += LLTrans::getString("worn");
 		item_state = IS_WORN;