diff --git a/.gitignore b/.gitignore
index dc0470a..e043c9f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -203,6 +203,11 @@ PublishScripts/
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
+# and except a Unity project's Packages/, which is source: Unity's package manifest and its
+# resolved lock file are both meant to be committed, and a NuGet restore folder never contains
+# a file by either name.
+!**/[Pp]ackages/manifest.json
+!**/[Pp]ackages/packages-lock.json
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
@@ -651,3 +656,16 @@ Temporary Items
# ImGui.ini files
imgui.ini
+
+# Game engine projects
+#
+# Godot: the import cache, and the mono/temp bin+obj a C# build writes.
+.godot/
+
+# Unity: .meta files are source, not the Visual Studio C++ build artifact that the `*.meta` rule
+# further up targets. Unity generates one per asset and it carries the GUID that scenes, prefabs
+# and serialized references point at, so ignoring them gives every clone fresh GUIDs and silently
+# breaks those references - including for a plug-in whose .dll is itself a build output. This
+# negation has to come after that rule to win, and is scoped to the asset tree so the Visual
+# Studio artifact stays ignored everywhere else.
+!**/[Aa]ssets/**/*.meta
diff --git a/DeepClone.Test/DictionaryComparerTests.cs b/DeepClone.Test/DictionaryComparerTests.cs
new file mode 100644
index 0000000..0c1f2f1
--- /dev/null
+++ b/DeepClone.Test/DictionaryComparerTests.cs
@@ -0,0 +1,125 @@
+// Copyright (c) 2023-2026 ktsu-dev contributors
+
+namespace ktsu.DeepClone.Test;
+
+using System.Collections.Concurrent;
+using System.Collections.Immutable;
+
+///
+/// Tests that cloning a dictionary through or
+/// keeps the source's key comparer for every
+/// dictionary type that exposes one (ktsu-dev/DeepClone#83).
+///
+[TestClass]
+public class DictionaryComparerTests
+{
+ private static readonly string[] DescendingKeys = ["d", "c", "b", "a"];
+
+ ///
+ /// Tests that a concurrent dictionary cloned through IDictionary keeps a case-insensitive comparer
+ /// and stays concurrent.
+ ///
+ [TestMethod]
+ public void ConcurrentDictionary_ThroughIDictionary_KeepsComparer()
+ {
+ ConcurrentDictionary original = new(StringComparer.OrdinalIgnoreCase);
+ original["Key"] = 1;
+
+ IDictionary clone = ((IDictionary)original).DeepClone();
+
+ Assert.IsInstanceOfType>(clone);
+ Assert.IsTrue(clone.ContainsKey("key"));
+ }
+
+ ///
+ /// Tests that a concurrent dictionary using reference equality, holding two equal but distinct keys,
+ /// clones without throwing and keeps both entries.
+ ///
+ [TestMethod]
+ public void ConcurrentDictionary_WithReferenceEquality_ClonesEqualKeys()
+ {
+ ConcurrentDictionary