Repository: PrefectHQ/prefect

Issue:
Diagnostics displays values that do not differ from defaults
## Current behavior
<!-- Please describe how the feature works today -->

If you copy the `prefect/src/prefect/config.toml` file to a local copy at `~/.prefect/config.toml` to easily have all the settings available, `prefect diagnostics` will display all of the settings as non-default even if they have not been changed.

## Proposed behavior
<!-- Please describe your proposed change to the current behavior -->

Resolving the differences between your `config.toml` and the built-in config should be relatively straightforward and would display more helpful diagnostic information.

## Example
<!-- Please give an example of how the enhancement would be useful -->

- User copies config file
- User posts diagnostics
- Support gets confused about all the non-defaults and cannot find what has actually changed

vs

- Only truly changed values are displayed
- Support has a really nice day

Comments:

Patch:
diff --git a/changes/pr3593.yaml b/changes/pr3593.yaml
new file mode 100644
index 0000000000..e65bcec38a
--- /dev/null
+++ b/changes/pr3593.yaml
@@ -0,0 +1,2 @@
+enhancement:
+  - "`prefect diagnostics` no longer displays keys that have values matching the default config - [#3593](https://github.com/PrefectHQ/prefect/pull/3593)"
diff --git a/docs/orchestration/agents/overview.md b/docs/orchestration/agents/overview.md
index 57c6762583..69f6e9bfbf 100644
--- a/docs/orchestration/agents/overview.md
+++ b/docs/orchestration/agents/overview.md
@@ -16,6 +16,12 @@ Once the agent submits the flow run for execution, the agent returns to waiting
 
 If for any reason the agent encounters an issue deploying the flow run for execution then it will mark that flow run as `Failed` with the message set to the error it encountered.
 
+:::tip Agent tracking
+Agents are tracked and uniquely identified by Prefect Cloud or Prefect Server through a combination of agent name, labels, token <Badge text="Cloud"/>, and Core Version.
+
+Prefect doesn't talk to agents but instead relies on agents to talk to it, which means agents identify themselves and communicate their settings. Independent agent processes with the same configurations will be interpreted by Prefect as the same agent; changing any piece of the configuration will tell Prefect to track these agents independently.
+:::
+
 ### Installation
 
 If Prefect is already [installed](../../core/getting_started/installation.html) no additional work is required to begin using Prefect agents!
@@ -97,7 +103,7 @@ Setting labels through the `PREFECT__CLOUD__AGENT__LABELS` environment variable
 
 Agents can optionally run a private HTTP server for use as a health check.
 Health checks can be used by common orchestration services (e.g.
-``supervisord``, ``docker``, ``kubernetes``, ...) to check that the agent is
+`supervisord`, `docker`, `kubernetes`, ...) to check that the agent is
 running properly and take actions (such as restarting the agent) if it's not.
 
 A few ways to enable:
@@ -114,8 +120,8 @@ $ prefect agent start --agent-address http://localhost:8080
 $ export PREFECT__CLOUD__AGENT__AGENT_ADDRESS=http://localhost:8080
 ```
 
-- Setting ``cloud.agent.agent_address`` in your [configuration](../../core/concepts/configuration.html):
+- Setting `cloud.agent.agent_address` in your [configuration](../../core/concepts/configuration.html):
 
-If enabled, the HTTP health check will be available via the ``/api/health``
-route at the configured agent address. This route returns ``200 OK`` if the
+If enabled, the HTTP health check will be available via the `/api/health`
+route at the configured agent address. This route returns `200 OK` if the
 agent is running and health, and will error otherwise.
diff --git a/src/prefect/utilities/collections.py b/src/prefect/utilities/collections.py
index 048d2cfd7f..4eee035892 100644
--- a/src/prefect/utilities/collections.py
+++ b/src/prefect/utilities/collections.py
@@ -1,10 +1,11 @@
 import collections
 from collections.abc import MutableMapping
-from typing import Any, Generator, Iterable, Iterator, Union, cast
+from typing import Any, Generator, Iterable, Iterator, Union, cast, TypeVar, Type
 
 from box import Box
 
 DictLike = Union[dict, "DotDict"]
+D = TypeVar("D", bound=Union[dict, MutableMapping])
 
 
 def flatten_seq(seq: Iterable) -> Generator:
@@ -181,7 +182,7 @@ class CompoundKey(tuple):
     pass
 
 
-def dict_to_flatdict(dct: dict, parent: CompoundKey = None) -> dict:
+def dict_to_flatdict(dct: DictLike, parent: CompoundKey = None) -> dict:
     """Converts a (nested) dictionary to a flattened representation.
 
     Each key of the flat dict will be a CompoundKey tuple containing the "chain of keys"
@@ -207,19 +208,19 @@ def dict_to_flatdict(dct: dict, parent: CompoundKey = None) -> dict:
     return dict(items)
 
 
-def flatdict_to_dict(dct: dict, dct_class: type = None) -> MutableMapping:
+def flatdict_to_dict(dct: dict, dct_class: Type[D] = None) -> D:
     """Converts a flattened dictionary back to a nested dictionary.
 
     Args:
         - dct (dict): The dictionary to be nested. Each key should be a
-        `CompoundKey`, as generated by `dict_to_flatdict()`
+            `CompoundKey`, as generated by `dict_to_flatdict()`
         - dct_class (type, optional): the type of the result; defaults to `dict`
 
     Returns:
-        - MutableMapping: A `MutableMapping` used to represent a nested dictionary
+        - D: An instance of `dct_class` used to represent a nested dictionary, bounded
+            as a MutableMapping or dict
     """
-
-    result = (dct_class or dict)()  # type: MutableMapping
+    result = cast(D, (dct_class or dict)())
     for k, v in dct.items():
         if isinstance(k, CompoundKey):
             current_dict = result
diff --git a/src/prefect/utilities/diagnostics.py b/src/prefect/utilities/diagnostics.py
index e02673b1b7..16211fe21f 100644
--- a/src/prefect/utilities/diagnostics.py
+++ b/src/prefect/utilities/diagnostics.py
@@ -25,11 +25,14 @@ def system_information() -> dict:
 
 def config_overrides(include_secret_names: bool = False) -> dict:
     """
-    Get user configuration overrides
+    Get user configuration keys that differ from the default configuration. Will only
+    return an indication if a key is set and differs from the defaults, values are
+    *not* returned.
 
     Args:
-        - include_secret_names (bool, optional): toggle output of Secret names, defaults to False.
-            Note: Secret values are never returned, only their names.
+        - include_secret_names (bool, optional): toggle inclusion of secret config keys
+            in the output. Note that secret values are never returned, only their names
+            when this is `True`. Defaults to `False`.
 
     Returns:
         - dict: a dictionary containing names of user configuration overrides
@@ -45,13 +48,42 @@ def config_overrides(include_secret_names: bool = False) -> dict:
             }
         return True
 
+    # Load the default config to compare values
+    default_config = dict()
+    default_config_path = prefect.configuration.DEFAULT_CONFIG
+    if default_config_path and os.path.isfile(default_config_path):
+        default_config = prefect.configuration.load_toml(default_config_path)
+
     user_config = dict()  # type: ignore
     user_config_path = prefect.configuration.USER_CONFIG
     if user_config_path and os.path.isfile(
         str(prefect.configuration.interpolate_env_vars(user_config_path))
     ):
         user_config = prefect.configuration.load_toml(user_config_path)
-        user_config = _replace_values(user_config)
+
+    # Create some shorter names for fully specified imports avoiding circular
+    # dependencies in the utilities
+    dict_to_flatdict = prefect.utilities.collections.dict_to_flatdict
+    flatdict_to_dict = prefect.utilities.collections.flatdict_to_dict
+
+    # Drop keys from `user_config` that have values identical to `default_config`
+    # converting to flat dictionaries for ease of comparison
+    user_config = dict_to_flatdict(user_config)
+    default_config = dict_to_flatdict(default_config)
+
+    # Collect keys to drop in a list so we aren't dropping keys during iteration
+    keys_to_drop = [
+        key
+        for key, val in user_config.items()
+        if key in default_config and val == default_config[key]
+    ]
+
+    for key in keys_to_drop:
+        user_config.pop(key)
+
+    # Restore to a nested dictionary then replace values with bools
+    user_config = flatdict_to_dict(user_config)
+    user_config = _replace_values(user_config)
 
     return dict(config_overrides=user_config)
 


Test patch:
diff --git a/tests/utilities/test_diagnostics.py b/tests/utilities/test_diagnostics.py
index b52d1daf04..f86cb74d4f 100644
--- a/tests/utilities/test_diagnostics.py
+++ b/tests/utilities/test_diagnostics.py
@@ -38,6 +38,39 @@ def test_config_overrides_populated(monkeypatch):
         assert config_overrides["config_overrides"] == {"debug": True}
 
 
+def test_config_overrides_excludes_all_default_matches(monkeypatch):
+    monkeypatch.setattr(
+        "prefect.configuration.USER_CONFIG", prefect.configuration.DEFAULT_CONFIG
+    )
+
+    config_overrides = diagnostics.config_overrides()
+
+    assert config_overrides["config_overrides"] == {}
+
+
+def test_config_overrides_excludes_some_default_matches(monkeypatch, tmpdir):
+    # Load and modify the default config
+    default_config = prefect.configuration.load_toml(
+        prefect.configuration.DEFAULT_CONFIG
+    )
+    default_config["debug"] = True
+    default_config["cloud"]["agent"]["name"] = "foo"
+
+    # Write it as a new user config
+    user_config_path = str(tmpdir.join("config.toml"))
+    file = open(user_config_path, "w+")
+    toml.dump(default_config, file)
+    file.close()
+    monkeypatch.setattr("prefect.configuration.USER_CONFIG", user_config_path)
+
+    config_overrides = diagnostics.config_overrides()
+
+    assert config_overrides["config_overrides"] == {
+        "debug": True,
+        "cloud": {"agent": {"name": True}},
+    }
+
+
 def test_config_overrides_secrets(monkeypatch):
     with tempfile.TemporaryDirectory() as tempdir:
         file = open("{}/config.toml".format(tempdir), "w+")