diff --git a/indra/llcommon/llsdutil.cpp b/indra/llcommon/llsdutil.cpp
index 803417d368b899d81a9cc8c8aca09e925103a097..562fd26658e6e30346ef026e8c916222a8c8acd4 100755
--- a/indra/llcommon/llsdutil.cpp
+++ b/indra/llcommon/llsdutil.cpp
@@ -182,7 +182,7 @@ char* ll_pretty_print_sd_ptr(const LLSD* sd)
 
 char* ll_pretty_print_sd(const LLSD& sd)
 {
-	const U32 bufferSize = 10 * 1024;
+	const U32 bufferSize = 100 * 1024;
 	static char buffer[bufferSize];
 	std::ostringstream stream;
 	//stream.rdbuf()->pubsetbuf(buffer, bufferSize);
diff --git a/indra/llinventory/llinventory.cpp b/indra/llinventory/llinventory.cpp
index a529aa3af3921a0ff646dd2e9b9be72e58cf2d7d..77b837f8ac6a275c5f6e8f2d5c4fc1346e49153e 100755
--- a/indra/llinventory/llinventory.cpp
+++ b/indra/llinventory/llinventory.cpp
@@ -50,6 +50,7 @@ static const std::string INV_DESC_LABEL("desc");
 static const std::string INV_PERMISSIONS_LABEL("permissions");
 static const std::string INV_SHADOW_ID_LABEL("shadow_id");
 static const std::string INV_ASSET_ID_LABEL("asset_id");
+static const std::string INV_LINKED_ID_LABEL("linked_id");
 static const std::string INV_SALE_INFO_LABEL("sale_info");
 static const std::string INV_FLAGS_LABEL("flags");
 static const std::string INV_CREATION_DATE_LABEL("created_at");
@@ -1109,6 +1110,11 @@ bool LLInventoryItem::fromLLSD(const LLSD& sd)
 	{
 		mAssetUUID = sd[w];
 	}
+	w = INV_LINKED_ID_LABEL;
+	if (sd.has(w))
+	{
+		mAssetUUID = sd[w];
+	}
 	w = INV_ASSET_TYPE_LABEL;
 	if (sd.has(w))
 	{
diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp
index d817f10aeeec3abb4707c1fd1f69b1b548455386..722587ec0eba11a760fe68593d274e442b47d5cf 100755
--- a/indra/newview/llappearancemgr.cpp
+++ b/indra/newview/llappearancemgr.cpp
@@ -397,6 +397,12 @@ class LLCallAfterInventoryCopyMgr: public LLCallAfterInventoryBatchMgr
 		LLCallAfterInventoryBatchMgr(dst_cat_id, phase_name, on_completion_func, on_failure_func, retry_after, max_retries)
 	{
 		addItems(src_items);
+		sInstanceCount++;
+	}
+
+	~LLCallAfterInventoryCopyMgr()
+	{
+		sInstanceCount--;
 	}
 	
 	virtual bool requestOperation(const LLUUID& item_id)
@@ -419,8 +425,15 @@ class LLCallAfterInventoryCopyMgr: public LLCallAfterInventoryBatchMgr
 			);
 		return true;
 	}
+
+	static S32 getInstanceCount() { return sInstanceCount; }
+	
+private:
+	static S32 sInstanceCount;
 };
 
+S32 LLCallAfterInventoryCopyMgr::sInstanceCount = 0;
+
 LLUpdateAppearanceOnDestroy::LLUpdateAppearanceOnDestroy(bool update_base_outfit_ordering,
 														 bool enforce_item_restrictions,
 														 bool enforce_ordering):
@@ -1609,25 +1622,6 @@ void LLAppearanceMgr::purgeBaseOutfitLink(const LLUUID& category, LLPointer<LLIn
 	}
 }
 
-void LLAppearanceMgr::removeCategoryContents(const LLUUID& category, bool keep_outfit_links,
-											 LLPointer<LLInventoryCallback> cb)
-{
-	LLInventoryModel::cat_array_t cats;
-	LLInventoryModel::item_array_t items;
-	gInventory.collectDescendents(category, cats, items,
-								  LLInventoryModel::EXCLUDE_TRASH);
-	for (S32 i = 0; i < items.count(); ++i)
-	{
-		LLViewerInventoryItem *item = items.get(i);
-		if (keep_outfit_links && (item->getActualType() == LLAssetType::AT_LINK_FOLDER))
-			continue;
-		if (item->getIsLinkType())
-		{
-			remove_inventory_item(item->getUUID(), cb);
-		}
-	}
-}
-
 // Keep the last N wearables of each type.  For viewer 2.0, N is 1 for
 // both body parts and clothing items.
 void LLAppearanceMgr::filterWearableItems(
@@ -1691,6 +1685,11 @@ void LLAppearanceMgr::removeAll(LLInventoryModel::item_array_t& items_to_kill,
 void LLAppearanceMgr::updateCOF(const LLUUID& category, bool append)
 {
 	LLViewerInventoryCategory *pcat = gInventory.getCategory(category);
+	if (!pcat)
+	{
+		llwarns << "no category found for id " << category << llendl;
+		return;
+	}
 	LL_INFOS("Avatar") << self_av_string() << "starting, cat '" << (pcat ? pcat->getName() : "[UNKNOWN]") << "'" << LL_ENDL;
 
 	const LLUUID cof = getCOF();
@@ -1756,6 +1755,7 @@ void LLAppearanceMgr::updateCOF(const LLUUID& category, bool append)
 
 	// Will link all the above items.
 	LLPointer<LLInventoryCallback> link_waiter = new LLUpdateAppearanceOnDestroy;
+#if 0
 	linkAll(cof,all_items,link_waiter);
 
 	// Add link to outfit if category is an outfit. 
@@ -1772,7 +1772,37 @@ void LLAppearanceMgr::updateCOF(const LLUUID& category, bool append)
 	// even in the non-append case, createBaseOutfitLink() already
 	// deletes the existing link, don't need to do it again here.
 	bool keep_outfit_links = true;
-	removeCategoryContents(cof, keep_outfit_links, link_waiter);
+	remove_folder_contents(cof, keep_outfit_links, link_waiter);
+#else
+	LLSD contents = LLSD::emptyArray();
+	for (LLInventoryModel::item_array_t::const_iterator it = all_items.begin();
+		 it != all_items.end(); ++it)
+	{
+		LLSD item_contents;
+		LLInventoryItem *item = *it;
+		item_contents["name"] = item->getName();
+		item_contents["desc"] = item->getActualDescription();
+		item_contents["linked_id"] = item->getLinkedUUID();
+		item_contents["type"] = LLAssetType::AT_LINK; 
+		contents.append(item_contents);
+	}
+	const LLUUID& base_id = append ? getBaseOutfitUUID() : category;
+	LLViewerInventoryCategory *base_cat = gInventory.getCategory(base_id);
+	if (base_cat)
+	{
+		LLSD base_contents;
+		base_contents["name"] = base_cat->getName();
+		base_contents["desc"] = "";
+		base_contents["linked_id"] = base_cat->getLinkedUUID();
+		base_contents["type"] = LLAssetType::AT_LINK_FOLDER; 
+		contents.append(base_contents);
+	}
+	if (gSavedSettings.getBOOL("DebugAvatarAppearanceMessage"))
+	{
+		dump_sequential_xml(gAgentAvatarp->getFullname() + "_slam_request", contents);
+	}
+	slam_inventory_folder(getCOF(), contents, link_waiter);
+#endif
 
 	LL_DEBUGS("Avatar") << self_av_string() << "waiting for LLUpdateAppearanceOnDestroy" << LL_ENDL;
 }
@@ -2154,6 +2184,11 @@ void LLAppearanceMgr::wearInventoryCategory(LLInventoryCategory* category, bool
 														   category->getUUID(), copy, append));
 }
 
+S32 LLAppearanceMgr::getActiveCopyOperations() const
+{
+	return LLCallAfterInventoryCopyMgr::getInstanceCount(); 
+}
+
 void LLAppearanceMgr::wearCategoryFinal(LLUUID& cat_id, bool copy_items, bool append)
 {
 	LL_INFOS("Avatar") << self_av_string() << "starting" << LL_ENDL;
@@ -2758,7 +2793,7 @@ bool LLAppearanceMgr::updateBaseOutfit()
 	updateClothingOrderingInfo();
 
 	// in a Base Outfit we do not remove items, only links
-	removeCategoryContents(base_outfit_id, false, NULL);
+	remove_folder_contents(base_outfit_id, false, NULL);
 
 	LLPointer<LLInventoryCallback> dirty_state_updater =
 		new LLBoostFuncInventoryCallback(no_op_inventory_func, appearance_mgr_update_dirty_state);
@@ -2918,7 +2953,7 @@ class RequestAgentUpdateAppearanceResponder: public LLHTTPClient::Responder
 			//LL_DEBUGS("Avatar") << dumpResponse() << LL_ENDL;
 			if (gSavedSettings.getBOOL("DebugAvatarAppearanceMessage"))
 			{
-				dumpContents(gAgentAvatarp->getFullname() + "_appearance_request_ok", content);
+				dump_sequential_xml(gAgentAvatarp->getFullname() + "_appearance_request_ok", content);
 			}
 		}
 		else
@@ -2936,7 +2971,7 @@ class RequestAgentUpdateAppearanceResponder: public LLHTTPClient::Responder
 		if (gSavedSettings.getBOOL("DebugAvatarAppearanceMessage"))
 		{
 			const LLSD& content = getContent();
-			dumpContents(gAgentAvatarp->getFullname() + "_appearance_request_error", content);
+			dump_sequential_xml(gAgentAvatarp->getFullname() + "_appearance_request_error", content);
 			debugCOF(content);
 		}
 		onFailure();
@@ -2960,15 +2995,6 @@ class RequestAgentUpdateAppearanceResponder: public LLHTTPClient::Responder
 		}
 	}	
 
-	void dumpContents(const std::string outprefix, const LLSD& content)
-	{
-		std::string outfilename = get_sequential_numbered_file_name(outprefix,".xml");
-		std::string fullpath = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,outfilename);
-		std::ofstream ofs(fullpath.c_str(), std::ios_base::out);
-		ofs << LLSDOStreamer<LLSDXMLFormatter>(content, LLSDFormatter::OPTIONS_PRETTY);
-		LL_DEBUGS("Avatar") << "results saved to: " << fullpath << LL_ENDL;
-	}
-
 	void debugCOF(const LLSD& content)
 	{
 		LL_INFOS("Avatar") << "AIS COF, version received: " << content["expected"].asInteger()
diff --git a/indra/newview/llappearancemgr.h b/indra/newview/llappearancemgr.h
index b63e883426076d0151d3f647e03cf74a6b279ecc..9eb26767c4711553ff1ff9db08d9eb9d20192e89 100755
--- a/indra/newview/llappearancemgr.h
+++ b/indra/newview/llappearancemgr.h
@@ -71,6 +71,8 @@ class LLAppearanceMgr: public LLSingleton<LLAppearanceMgr>
 									  LLInventoryModel::item_array_t& items_to_kill);
 	void enforceCOFItemRestrictions(LLPointer<LLInventoryCallback> cb);
 
+	S32 getActiveCopyOperations() const;
+	
 	// Copy all items and the src category itself.
 	void shallowCopyCategory(const LLUUID& src_id, const LLUUID& dst_id,
 							 LLPointer<LLInventoryCallback> cb);
@@ -235,8 +237,6 @@ class LLAppearanceMgr: public LLSingleton<LLAppearanceMgr>
 								   LLInventoryModel::item_array_t& obj_items,
 								   LLInventoryModel::item_array_t& gest_items);
 
-	void removeCategoryContents(const LLUUID& category, bool keep_outfit_links,
-								LLPointer<LLInventoryCallback> cb);
 	static void onOutfitRename(const LLSD& notification, const LLSD& response);
 
 	void setOutfitLocked(bool locked);
diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp
index 38fa3c36e3863b5a02fe09f1bc1082e6e2297df0..c0c48d6695bfbae0b9628f9235aa5e3dc501a2ab 100755
--- a/indra/newview/llinventorymodel.cpp
+++ b/indra/newview/llinventorymodel.cpp
@@ -1170,79 +1170,193 @@ void parse_llsd_uuid_array(const LLSD& content, const std::string& name, uuid_ve
 
 void LLInventoryModel::onAISUpdateReceived(const std::string& context, const LLSD& update)
 {
-	LL_DEBUGS("Inventory") << "ais update " << context << ":" << ll_pretty_print_sd(update) << llendl;
+	if (gSavedSettings.getBOOL("DebugAvatarAppearanceMessage"))
+	{
+		dump_sequential_xml(gAgentAvatarp->getFullname() + "_ais_update", update);
+	}
 
+	// Track changes to descendent counts for accounting.
+	std::map<LLUUID,S32> cat_deltas;
+	typedef std::map<LLUUID,LLPointer<LLViewerInventoryItem> > deferred_item_map_t;
+	deferred_item_map_t items_created;
+	deferred_item_map_t items_updated;
+	std::set<LLUUID> objects_deleted;
+
+	// parse _categories_removed -> objects_deleted
 	uuid_vec_t cat_ids;
 	parse_llsd_uuid_array(update,"_categories_removed",cat_ids);
 	for (uuid_vec_t::const_iterator it = cat_ids.begin();
 		 it != cat_ids.end(); ++it)
 	{
-		onObjectDeletedFromServer(*it, false);
+		LLViewerInventoryCategory *cat = getCategory(*it);
+		cat_deltas[cat->getParentUUID()]--;
+		objects_deleted.insert(*it);
 	}
 
+	// parse _categories_items_removed -> objects_deleted
 	uuid_vec_t item_ids;
 	parse_llsd_uuid_array(update,"_category_items_removed",item_ids);
 	for (uuid_vec_t::const_iterator it = item_ids.begin();
 		 it != item_ids.end(); ++it)
 	{
-		onObjectDeletedFromServer(*it, false);
+		LLViewerInventoryItem *item = getItem(*it);
+		cat_deltas[item->getParentUUID()]--;
+		objects_deleted.insert(*it);
 	}
 
+	// parse _broken_links_removed -> objects_deleted
 	uuid_vec_t broken_link_ids;
 	parse_llsd_uuid_array(update,"_broken_links_removed",broken_link_ids);
 	for (uuid_vec_t::const_iterator it = broken_link_ids.begin();
 		 it != broken_link_ids.end(); ++it)
 	{
-		onObjectDeletedFromServer(*it, false);
+		LLViewerInventoryItem *item = getItem(*it);
+		cat_deltas[item->getParentUUID()]--;
+		objects_deleted.insert(*it);
 	}
 
-	if (update.has("item_id"))
+	// parse _created_items
+	uuid_vec_t created_item_ids;
+	parse_llsd_uuid_array(update,"_created_items",created_item_ids);
+
+	if (update.has("_embedded"))
 	{
-		// item has been modified or possibly created (would be better if we could distinguish these cases directly)
-		LLUUID item_id = update["item_id"].asUUID();
-		LLViewerInventoryItem *item = gInventory.getItem(item_id);
-		LLViewerInventoryCategory *cat = gInventory.getCategory(item_id);
-		if (item)
+		const LLSD& embedded = update["_embedded"];
+		for(LLSD::map_const_iterator it = embedded.beginMap(),
+				end = embedded.endMap();
+				it != end; ++it)
 		{
-			LLSD changes;
-			if (update.has("name") && update["name"] != item->getName())
+			const std::string& field = (*it).first;
+
+			// parse created links
+			if (field == "link")
 			{
-				changes["name"] = update["name"];
-			}
-			if (update.has("desc") && update["desc"] != item->getActualDescription())
+				const LLSD& links = embedded["link"];
+				for(LLSD::map_const_iterator linkit = links.beginMap(),
+						linkend = links.endMap();
+					linkit != linkend; ++linkit)
+				{
+					const LLUUID link_id((*linkit).first);
+					const LLSD& link_map = (*linkit).second;
+					uuid_vec_t::const_iterator pos =
+						std::find(created_item_ids.begin(),
+								  created_item_ids.end(),link_id);
+					if (pos != created_item_ids.end())
+					{
+						LLPointer<LLViewerInventoryItem> new_link(new LLViewerInventoryItem);
+						BOOL rv = new_link->unpackMessage(link_map);
+						if (rv)
+						{
+							items_created[link_id] = new_link;
+							const LLUUID& parent_id = new_link->getParentUUID();
+							cat_deltas[parent_id]++;
+						}
+						else
+						{
+							llwarns << "failed to unpack" << llendl;
+						}
+					}
+					else
+					{
+						LL_DEBUGS("Inventory") << "Ignoring link not in created items list " << link_id << llendl;
+					}
+				}
+		}
+			else
 			{
-				changes["desc"] = update["desc"];
+				llwarns << "unrecognized embedded field " << field << llendl;
 			}
-			onItemUpdated(item_id,changes,true);
 		}
-		else if (cat)
+		
+	}
+
+	// Parse item update at the top level.
+	if (update.has("item_id"))
+	{
+		LLUUID item_id = update["item_id"].asUUID();
+		LLPointer<LLViewerInventoryItem> new_item(new LLViewerInventoryItem);
+		BOOL rv = new_item->unpackMessage(update);
+		if (rv)
 		{
-			llerrs << "don't handle cat update yet" << llendl;
+			items_updated[item_id] = new_item;
+			// This statement is here to cause a new entry with 0
+			// delta to be created if it does not already exist;
+			// otherwise has no effect.
+			cat_deltas[new_item->getParentUUID()];
 		}
 		else
 		{
-			llerrs << "don't handle creation case yet" << llendl;
+			llerrs << "unpack failed" << llendl;
 		}
-	
 	}
 
+	// Do descendent/version accounting.
+	// Can remove this if/when we use the version info directly.
+	for (std::map<LLUUID,S32>::const_iterator catit = cat_deltas.begin();
+		 catit != cat_deltas.end(); ++catit)
+	{
+		const LLUUID cat_id(catit->first);
+		S32 delta = catit->second;
+		LLInventoryModel::LLCategoryUpdate up(cat_id, delta);
+		gInventory.accountForUpdate(up);
+	}
+	
 	// TODO - how can we use this version info? Need to be sure all
 	// changes are going through AIS first, or at least through
 	// something with a reliable responder.
-#if 0
 	const std::string& ucv = "_updated_category_versions";
 	if (update.has(ucv))
 	{
 		for(LLSD::map_const_iterator it = update[ucv].beginMap(),
 				end = update[ucv].endMap();
-				it != end; ++it)
+			it != end; ++it)
 		{
 			const LLUUID id((*it).first);
 			S32 version = (*it).second.asInteger();
+			LLViewerInventoryCategory *cat = gInventory.getCategory(id);
+			if (cat->getVersion() != version)
+			{
+				llwarns << "Possible version mismatch, viewer " << cat->getVersion()
+						<< " server " << version << llendl;
+			}
 		}
 	}
-#endif
+
+	// CREATE ITEMS
+	for (deferred_item_map_t::const_iterator create_it = items_created.begin();
+		 create_it != items_created.end(); ++create_it)
+	{
+		LLUUID item_id(create_it->first);
+		LLPointer<LLViewerInventoryItem> new_item = create_it->second;
+
+		// FIXME risky function since it calls updateServer() in some
+		// cases.  Maybe break out the update/create cases, in which
+		// case this is create.
+		LL_DEBUGS("Inventory") << "created item " << item_id << llendl;
+		gInventory.updateItem(new_item);
+	}
 	
+	// UPDATE ITEMS
+	for (deferred_item_map_t::const_iterator update_it = items_updated.begin();
+		 update_it != items_updated.end(); ++update_it)
+	{
+		LLUUID item_id(update_it->first);
+		LLPointer<LLViewerInventoryItem> new_item = update_it->second;
+		// FIXME risky function since it calls updateServer() in some
+		// cases.  Maybe break out the update/create cases, in which
+		// case this is update.
+		LL_DEBUGS("Inventory") << "updated item " << item_id << llendl;
+		gInventory.updateItem(new_item);
+	}
+
+	// DELETE OBJECTS
+	for (std::set<LLUUID>::const_iterator del_it = objects_deleted.begin();
+		 del_it != objects_deleted.end(); ++del_it)
+	{
+		LL_DEBUGS("Inventory") << "deleted item " << *del_it << llendl;
+		onObjectDeletedFromServer(*del_it, false, false);
+	}
+
 }
 
 void LLInventoryModel::onItemUpdated(const LLUUID& item_id, const LLSD& updates, bool update_parent_version)
@@ -1395,7 +1509,7 @@ void LLInventoryModel::onDescendentsPurgedFromServer(const LLUUID& object_id, bo
 
 // Update model after an item is confirmed as removed from
 // server. Works for categories or items.
-void LLInventoryModel::onObjectDeletedFromServer(const LLUUID& object_id, bool fix_broken_links)
+void LLInventoryModel::onObjectDeletedFromServer(const LLUUID& object_id, bool fix_broken_links, bool update_parent_version)
 {
 	LLPointer<LLInventoryObject> obj = getObject(object_id);
 	if(obj)
@@ -1406,9 +1520,13 @@ void LLInventoryModel::onObjectDeletedFromServer(const LLUUID& object_id, bool f
 			onDescendentsPurgedFromServer(object_id, fix_broken_links);
 		}
 
+
 		// From item/cat removeFromServer()
-		LLInventoryModel::LLCategoryUpdate up(obj->getParentUUID(), -1);
-		accountForUpdate(up);
+		if (update_parent_version)
+		{
+			LLInventoryModel::LLCategoryUpdate up(obj->getParentUUID(), -1);
+			accountForUpdate(up);
+		}
 
 		// From purgeObject()
 		LLPreview::hide(object_id);
diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h
index fd2481b5317435e242ff3739f0d2a6439f90bb6c..a41a824906653eabfaf0b174b365283aa4b6b441 100755
--- a/indra/newview/llinventorymodel.h
+++ b/indra/newview/llinventorymodel.h
@@ -334,7 +334,7 @@ class LLInventoryModel
 		
 	// Update model after an item is confirmed as removed from
 	// server. Works for categories or items.
-	void onObjectDeletedFromServer(const LLUUID& item_id, bool fix_broken_links = true);
+	void onObjectDeletedFromServer(const LLUUID& item_id, bool fix_broken_links = true, bool update_parent_version = true);
 
 	// Update model after all descendents removed from server.
 	void onDescendentsPurgedFromServer(const LLUUID& object_id, bool fix_broken_links = true);
diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp
index 465a49d0048c0cbe1ec796ea9d6d485a0033d9c3..0608c46051c0e27edf981e9e82dc4053dfe8d627 100755
--- a/indra/newview/llviewerinventory.cpp
+++ b/indra/newview/llviewerinventory.cpp
@@ -493,6 +493,34 @@ class UpdateCategoryCommand: public AISCommand
 	LLSD mUpdates;
 };
 
+class SlamFolderCommand: public AISCommand
+{
+public:
+	SlamFolderCommand(const LLUUID& folder_id, const LLSD& contents, LLPointer<LLInventoryCallback> callback):
+		mContents(contents),
+		AISCommand(callback)
+	{
+		std::string cap;
+		if (!getCap(cap))
+		{
+			llwarns << "No cap found" << llendl;
+			return;
+		}
+		LLUUID tid;
+		tid.generate();
+		std::string url = cap + std::string("/category/") + folder_id.asString() + "/links?tid=" + tid.asString();
+		llinfos << url << llendl;
+		LLCurl::ResponderPtr responder = this;
+		LLSD headers;
+		headers["Content-Type"] = "application/llsd+xml";
+		F32 timeout = HTTP_REQUEST_EXPIRY_SECS;
+		command_func_type cmd = boost::bind(&LLHTTPClient::put, url, mContents, responder, headers, timeout);
+		setCommandFunc(cmd);
+	}
+private:
+	LLSD mContents;
+};
+
 ///----------------------------------------------------------------------------
 /// Class LLViewerInventoryItem
 ///----------------------------------------------------------------------------
@@ -1828,6 +1856,54 @@ void create_new_item(const std::string& name,
 	
 }	
 
+void slam_inventory_folder(const LLUUID& folder_id,
+						   const LLSD& contents,
+						   LLPointer<LLInventoryCallback> cb)
+{
+	std::string cap;
+	if (AISCommand::getCap(cap))
+	{
+		LLPointer<AISCommand> cmd_ptr = new SlamFolderCommand(folder_id, contents, cb);
+		cmd_ptr->run_command();
+	}
+	else // no cap
+	{
+		for (LLSD::array_const_iterator it = contents.beginArray();
+			 it != contents.endArray();
+			 ++it)
+		{
+			const LLSD& item_contents = *it;
+			link_inventory_item(gAgent.getID(),
+								item_contents["linked_id"].asUUID(),
+								folder_id,
+								item_contents["name"].asString(),
+								item_contents["desc"].asString(),
+								LLAssetType::EType(item_contents["type"].asInteger()),
+								cb);
+		}
+		remove_folder_contents(folder_id,false,cb);
+	}
+}
+
+void remove_folder_contents(const LLUUID& category, bool keep_outfit_links,
+							LLPointer<LLInventoryCallback> cb)
+{
+	LLInventoryModel::cat_array_t cats;
+	LLInventoryModel::item_array_t items;
+	gInventory.collectDescendents(category, cats, items,
+								  LLInventoryModel::EXCLUDE_TRASH);
+	for (S32 i = 0; i < items.count(); ++i)
+	{
+		LLViewerInventoryItem *item = items.get(i);
+		if (keep_outfit_links && (item->getActualType() == LLAssetType::AT_LINK_FOLDER))
+			continue;
+		if (item->getIsLinkType())
+		{
+			remove_inventory_item(item->getUUID(), cb);
+		}
+	}
+}
+
 const std::string NEW_LSL_NAME = "New Script"; // *TODO:Translate? (probably not)
 const std::string NEW_NOTECARD_NAME = "New Note"; // *TODO:Translate? (probably not)
 const std::string NEW_GESTURE_NAME = "New Gesture"; // *TODO:Translate? (probably not)
@@ -2263,3 +2339,5 @@ BOOL LLViewerInventoryItem::regenerateLink()
 	gInventory.notifyObservers();
 	return TRUE;
 }
+
+
diff --git a/indra/newview/llviewerinventory.h b/indra/newview/llviewerinventory.h
index c52b0c2d9d00a655d74e4db6e501457e0416f109..9af71dfc9c98f22d6e175f8ae47b54119de96b33 100755
--- a/indra/newview/llviewerinventory.h
+++ b/indra/newview/llviewerinventory.h
@@ -407,4 +407,11 @@ void menu_create_inventory_item(LLInventoryPanel* root,
 								const LLSD& userdata,
 								const LLUUID& default_parent_uuid = LLUUID::null);
 
+void slam_inventory_folder(const LLUUID& folder_id,
+						   const LLSD& contents,
+						   LLPointer<LLInventoryCallback> cb);
+
+void remove_folder_contents(const LLUUID& folder_id, bool keep_outfit_links,
+							  LLPointer<LLInventoryCallback> cb);
+
 #endif // LL_LLVIEWERINVENTORY_H
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index e77b29aca41c637cc2a24a4d965f45a7835ca3ad..b635087d66149a1d54125aa8b5c78cdfee0df406 100755
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -1877,7 +1877,7 @@ std::string LLViewerRegion::getCapability(const std::string& name) const
 {
 	if (!capabilitiesReceived() && (name!=std::string("Seed")) && (name!=std::string("ObjectMedia")))
 	{
-		llwarns << "getCapability called before caps received" << llendl;
+		llwarns << "getCapability called before caps received for " << name << llendl;
 	}
 	
 	CapabilityMap::const_iterator iter = mImpl->mCapabilities.find(name);
diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp
index b73411080a792793311a8b6d6f52212fe84f473c..68633fba6ea96ccbc9e7c7bfab7fd7df5011a6cd 100755
--- a/indra/newview/llviewerstats.cpp
+++ b/indra/newview/llviewerstats.cpp
@@ -734,44 +734,28 @@ void send_stats()
 	LLHTTPClient::post(url, body, new ViewerStatsResponder());
 }
 
-LLFrameTimer& LLViewerStats::PhaseMap::getPhaseTimer(const std::string& phase_name)
+LLTimer& LLViewerStats::PhaseMap::getPhaseTimer(const std::string& phase_name)
 {
 	phase_map_t::iterator iter = mPhaseMap.find(phase_name);
 	if (iter == mPhaseMap.end())
 	{
-		LLFrameTimer timer;
+		LLTimer timer;
 		mPhaseMap[phase_name] = timer;
 	}
-	LLFrameTimer& timer = mPhaseMap[phase_name];
+	LLTimer& timer = mPhaseMap[phase_name];
 	return timer;
 }
 
 void LLViewerStats::PhaseMap::startPhase(const std::string& phase_name)
 {
-	LLFrameTimer& timer = getPhaseTimer(phase_name);
-	lldebugs << "startPhase " << phase_name << llendl;
-	timer.unpause();
-}
-
-void LLViewerStats::PhaseMap::stopAllPhases()
-{
-	for (phase_map_t::iterator iter = mPhaseMap.begin();
-		 iter != mPhaseMap.end(); ++iter)
-	{
-		const std::string& phase_name = iter->first;
-		if (iter->second.getStarted())
-		{
-			// Going from started to paused state - record stats.
-			recordPhaseStat(phase_name,iter->second.getElapsedTimeF32());
-		}
-		lldebugs << "stopPhase (all) " << phase_name << llendl;
-		iter->second.pause();
-	}
+	LLTimer& timer = getPhaseTimer(phase_name);
+	timer.start();
+	LL_DEBUGS("Avatar") << "startPhase " << phase_name << llendl;
 }
 
 void LLViewerStats::PhaseMap::clearPhases()
 {
-	lldebugs << "clearPhases" << llendl;
+	LL_DEBUGS("Avatar") << "clearPhases" << llendl;
 
 	mPhaseMap.clear();
 }
@@ -796,7 +780,6 @@ LLViewerStats::PhaseMap::PhaseMap()
 {
 }
 
-
 void LLViewerStats::PhaseMap::stopPhase(const std::string& phase_name)
 {
 	phase_map_t::iterator iter = mPhaseMap.find(phase_name);
@@ -809,6 +792,7 @@ void LLViewerStats::PhaseMap::stopPhase(const std::string& phase_name)
 		}
 	}
 }
+
 // static
 LLViewerStats::StatsAccumulator& LLViewerStats::PhaseMap::getPhaseStats(const std::string& phase_name)
 {
@@ -832,14 +816,18 @@ void LLViewerStats::PhaseMap::recordPhaseStat(const std::string& phase_name, F32
 bool LLViewerStats::PhaseMap::getPhaseValues(const std::string& phase_name, F32& elapsed, bool& completed)
 {
 	phase_map_t::iterator iter = mPhaseMap.find(phase_name);
+	bool found = false;
 	if (iter != mPhaseMap.end())
 	{
+		found = true;
 		elapsed =  iter->second.getElapsedTimeF32();
 		completed = !iter->second.getStarted();
-		return true;
+		LL_DEBUGS("Avatar") << " phase_name " << phase_name << " elapsed " << elapsed << " completed " << completed << " timer addr " << (S32)(&iter->second) << llendl;
 	}
 	else
 	{
-		return false;
+		LL_DEBUGS("Avatar") << " phase_name " << phase_name << " NOT FOUND"  << llendl;
 	}
+
+	return found;
 }
diff --git a/indra/newview/llviewerstats.h b/indra/newview/llviewerstats.h
index 6b2461be41fb51fd9eca307590412fa09de80ad2..eaa0b6beff0da4eacb9a53bf2e033553e12d2bb7 100755
--- a/indra/newview/llviewerstats.h
+++ b/indra/newview/llviewerstats.h
@@ -279,7 +279,7 @@ class LLViewerStats : public LLSingleton<LLViewerStats>
 
 	// Phase tracking (originally put in for avatar rezzing), tracking
 	// progress of active/completed phases for activities like outfit changing.
-	typedef std::map<std::string,LLFrameTimer>	phase_map_t;
+	typedef std::map<std::string,LLTimer>	phase_map_t;
 	typedef std::map<std::string,StatsAccumulator>	phase_stats_t;
 	class PhaseMap
 	{
@@ -288,11 +288,10 @@ class LLViewerStats : public LLSingleton<LLViewerStats>
 		static phase_stats_t sStats;
 	public:
 		PhaseMap();
-		LLFrameTimer& 	getPhaseTimer(const std::string& phase_name);
+		LLTimer& 		getPhaseTimer(const std::string& phase_name);
 		bool 			getPhaseValues(const std::string& phase_name, F32& elapsed, bool& completed);
 		void			startPhase(const std::string& phase_name);
 		void			stopPhase(const std::string& phase_name);
-		void			stopAllPhases();
 		void			clearPhases();
 		LLSD			dumpPhases();
 		static StatsAccumulator& getPhaseStats(const std::string& phase_name);
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index 310ff47cf5c60174b73c5a445b595a61025bc1b9..4593541f357305fe2f41a09460431831ba6ac971 100755
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -6049,9 +6049,12 @@ void LLVOAvatar::clearPhases()
 
 void LLVOAvatar::startPhase(const std::string& phase_name)
 {
-	F32 elapsed;
-	bool completed;
-	if (getPhases().getPhaseValues(phase_name, elapsed, completed))
+	F32 elapsed = 0.0;
+	bool completed = false;
+	bool found = getPhases().getPhaseValues(phase_name, elapsed, completed);
+	//LL_DEBUGS("Avatar") << avString() << " phase state " << phase_name
+	//					<< " found " << found << " elapsed " << elapsed << " completed " << completed << llendl;
+	if (found)
 	{
 		if (!completed)
 		{
@@ -6065,9 +6068,12 @@ void LLVOAvatar::startPhase(const std::string& phase_name)
 
 void LLVOAvatar::stopPhase(const std::string& phase_name, bool err_check)
 {
-	F32 elapsed;
-	bool completed;
-	if (getPhases().getPhaseValues(phase_name, elapsed, completed))
+	F32 elapsed = 0.0;
+	bool completed = false;
+	bool found = getPhases().getPhaseValues(phase_name, elapsed, completed);
+	//LL_DEBUGS("Avatar") << avString() << " phase state " << phase_name
+	//					<< " found " << found << " elapsed " << elapsed << " completed " << completed << llendl;
+	if (found)
 	{
 		if (!completed)
 		{
@@ -7446,6 +7452,15 @@ std::string get_sequential_numbered_file_name(const std::string& prefix,
 	return outfilename;
 }
 
+void dump_sequential_xml(const std::string outprefix, const LLSD& content)
+{
+	std::string outfilename = get_sequential_numbered_file_name(outprefix,".xml");
+	std::string fullpath = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,outfilename);
+	std::ofstream ofs(fullpath.c_str(), std::ios_base::out);
+	ofs << LLSDOStreamer<LLSDXMLFormatter>(content, LLSDFormatter::OPTIONS_PRETTY);
+	LL_DEBUGS("Avatar") << "results saved to: " << fullpath << LL_ENDL;
+}
+
 void LLVOAvatar::dumpArchetypeXML(const std::string& prefix, bool group_by_wearables )
 {
 	std::string outprefix(prefix);
diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h
index 19ad49d3a13409577410faf2bbc0989fb33b6ad5..fad2fd962c66d35c2261fc1e2909ed29afe07ea4 100755
--- a/indra/newview/llvoavatar.h
+++ b/indra/newview/llvoavatar.h
@@ -998,6 +998,7 @@ extern const S32 MAX_TEXTURE_VIRTUAL_SIZE_RESET_INTERVAL;
 
 std::string get_sequential_numbered_file_name(const std::string& prefix,
 											  const std::string& suffix);
+void dump_sequential_xml(const std::string outprefix, const LLSD& content);
 void dump_visual_param(apr_file_t* file, LLVisualParam* viewer_param, F32 value);
 
 #endif // LL_VOAVATAR_H
diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp
index 5b6fcc5d27c3e621371c006e056fdbd86d28387e..232bf3e478322023a5df3184a61807ffea1be57f 100755
--- a/indra/newview/llvoavatarself.cpp
+++ b/indra/newview/llvoavatarself.cpp
@@ -239,6 +239,9 @@ void LLVOAvatarSelf::initInstance()
 
 bool LLVOAvatarSelf::checkStuckAppearance()
 {
+	const F32 CONDITIONAL_UNSTICK_INTERVAL = 300.0;
+	const F32 UNCONDITIONAL_UNSTICK_INTERVAL = 600.0;
+	
 	if (gAgentWearables.isCOFChangeInProgress())
 	{
 		LL_DEBUGS("Avatar") << "checking for stuck appearance" << llendl;
@@ -246,6 +249,14 @@ bool LLVOAvatarSelf::checkStuckAppearance()
 		LL_DEBUGS("Avatar") << "change in progress for " << change_time << " seconds" << llendl;
 		S32 active_hp = LLAppearanceMgr::instance().countActiveHoldingPatterns();
 		LL_DEBUGS("Avatar") << "active holding patterns " << active_hp << " seconds" << llendl;
+		S32 active_copies = LLAppearanceMgr::instance().getActiveCopyOperations();
+		LL_DEBUGS("Avatar") << "active copy operations " << active_copies << llendl;
+
+		if ((change_time > CONDITIONAL_UNSTICK_INTERVAL && active_copies == 0) ||
+			(change_time > UNCONDITIONAL_UNSTICK_INTERVAL))
+		{
+			gAgentWearables.notifyLoadingFinished();
+		}
 	}
 
 	// Return false to continue running check periodically.
@@ -2369,7 +2380,6 @@ LLSD summarize_by_buckets(std::vector<LLSD> in_records,
 
 void LLVOAvatarSelf::sendViewerAppearanceChangeMetrics()
 {
-	// gAgentAvatarp->stopAllPhases();
 	static volatile bool reporting_started(false);
 	static volatile S32 report_sequence(0);