diff --git a/changes/pr4189.yaml b/changes/pr4189.yaml
new file mode 100644
index 0000000000..4281dbf640
--- /dev/null
+++ b/changes/pr4189.yaml
@@ -0,0 +1,2 @@
+fix:
+  - "Make task slug generation robust to modifying existing task names - [#4189](https://github.com/PrefectHQ/prefect/pull/4189)"
diff --git a/src/prefect/core/flow.py b/src/prefect/core/flow.py
index d790330786..0789f336ab 100644
--- a/src/prefect/core/flow.py
+++ b/src/prefect/core/flow.py
@@ -4,6 +4,7 @@ import copy
 import functools
 import hashlib
 import inspect
+import itertools
 import json
 import os
 import tempfile
@@ -176,7 +177,10 @@ class Flow:
 
         self.tasks = set()  # type: Set[Task]
         self.edges = set()  # type: Set[Edge]
-        self.slugs = dict()  # type: Dict[Task, str]
+        self._slug_counters = collections.defaultdict(
+            lambda: itertools.count(1)
+        )  # type: Dict[str, Iterator[int]]
+        self.slugs = {}  # type: Dict[Task, str]
         self.constants = collections.defaultdict(
             dict
         )  # type: Dict[Task, Dict[str, Any]]
@@ -486,12 +490,14 @@ class Flow:
         Returns:
             - str: the corresponding slug
         """
-        slug_bases = []
-        for t in self.tasks:
-            slug_bases.append(f"{t.name}-" + "-".join(sorted(t.tags)))
-        new_slug = f"{task.name}-" + "-".join(sorted(task.tags))
-        index = slug_bases.count(new_slug)
-        return f"{new_slug}{'' if new_slug.endswith('-') else '-'}{index + 1}"
+        parts = [task.name]
+        parts.extend(sorted(task.tags))
+        prefix = "-".join(parts)
+        while True:
+            ind = next(self._slug_counters[prefix])
+            slug = f"{prefix}-{ind}"
+            if slug not in self.slugs.values():
+                return slug
 
     def add_task(self, task: Task) -> Task:
         """

diff --git a/tests/core/test_flow.py b/tests/core/test_flow.py
index f7df8b2f84..2c2144b6e0 100644
--- a/tests/core/test_flow.py
+++ b/tests/core/test_flow.py
@@ -3196,12 +3196,16 @@ def test_run_agent_passes_flow_labels(monkeypatch, kind):
 
 class TestSlugGeneration:
     def test_slugs_are_stable(self):
-        tasks = [Task(name=str(x)) for x in range(10)]
+        tasks = [Task(name="add") for _ in range(5)]
+        tasks.extend(Task(name="mul") for _ in range(5))
         flow_one = Flow("one", tasks=tasks)
         flow_two = Flow("two", tasks=tasks)
 
-        assert set(flow_one.slugs.values()) == set([str(x) + "-1" for x in range(10)])
-        assert flow_one.slugs == flow_two.slugs
+        sol = {f"add-{i}" for i in range(1, 6)}
+        sol.update(f"mul-{i}" for i in range(1, 6))
+
+        assert set(flow_one.slugs.values()) == sol
+        assert set(flow_two.slugs.values()) == sol
 
     def test_slugs_incorporate_tags_and_order(self):
         with Flow("one") as flow_one:
@@ -3224,3 +3228,27 @@ class TestSlugGeneration:
             "a-tag1-1",
             "b-tag1-tag2-1",
         }
+
+    def test_generated_slugs_dont_collide_with_user_provided_slugs(self):
+        with Flow("test") as flow:
+            a3 = Task("a", slug="a-3")
+            flow.add_task(a3)
+            a1 = Task("a")
+            flow.add_task(a1)
+            a2 = Task("a")
+            flow.add_task(a2)
+            a4 = Task("a")
+            flow.add_task(a4)
+
+        assert flow.slugs == {a1: "a-1", a2: "a-2", a3: "a-3", a4: "a-4"}
+
+    def test_slugs_robust_to_task_name_changes(self):
+        "See https://github.com/PrefectHQ/prefect/issues/4185"
+        with Flow("test") as flow:
+            a1 = Task("a")
+            flow.add_task(a1)
+            a1.name = "changed"
+            a2 = Task("a")
+            flow.add_task(a2)
+
+        assert flow.slugs == {a1: "a-1", a2: "a-2"}

Modifying `task.name` can violate the assumption that slugs are unique
## Description
Dynamically modifying a task's name can result in overlapping slugs. This seems to be because https://github.com/PrefectHQ/prefect/blob/20cd7352705741628ff83789e779b7bdb5315969/src/prefect/core/flow.py#L473-L494 generates `slug_bases` each time that the tasks are added.

Better practice is to provide the task name when it is added to the flow, but there is no enforcement that `.name` is read only, and the use of `task_args` is less intuitive.

`_generate_task_slug` could be made more robust, or `.name` could be made read-only or raise a warning suggesting the preferred method.


## Expected Behavior
Slugs should always be unique.


## Reproduction
```python
from prefect import Flow, Parameter, task

@task
def identity(x):
    return x

with Flow('slug-overlap') as flow:
    with tags('test-tag'):
        a = Parameter('a', default=1)
        # b = identity(a, task_args=dict(name='better-practice'))
        b = identity(a)
        b.name = 'bad-practice'
        c = identity(a)
print(flow.slugs)
```
results in 
```python
{<Task: bad-practice>: 'identity-test-tag-1', <Parameter: a>: 'a', <Task: identity>: 'identity-test-tag-1'}
```



## Environment
n/a
tests/core/test_flow.py::TestSlugGeneration::test_generated_slugs_dont_collide_with_user_provided_slugs tests/core/test_flow.py::TestSlugGeneration::test_slugs_robust_to_task_name_changes
tests/core/test_flow.py::TestCreateFlow::test_create_flow_with_no_args tests/core/test_flow.py::TestCreateFlow::test_create_flow_with_no_name tests/core/test_flow.py::TestCreateFlow::test_create_flow_with_name_as_none tests/core/test_flow.py::TestCreateFlow::test_create_flow_with_name_as_empty_string tests/core/test_flow.py::TestCreateFlow::test_create_flow_with_name_as_false tests/core/test_flow.py::TestCreateFlow::test_create_flow_with_name tests/core/test_flow.py::TestCreateFlow::test_create_flow_with_edges tests/core/test_flow.py::TestCreateFlow::test_create_flow_with_schedule tests/core/test_flow.py::TestCreateFlow::test_create_flow_without_state_handler tests/core/test_flow.py::TestCreateFlow::test_create_flow_with_on_failure tests/core/test_flow.py::TestCreateFlow::test_create_flow_with_state_handler[handlers0] tests/core/test_flow.py::TestCreateFlow::test_create_flow_with_state_handler[handlers1] tests/core/test_flow.py::TestCreateFlow::test_create_flow_illegal_handler tests/core/test_flow.py::TestCreateFlow::test_flow_has_logger tests/core/test_flow.py::TestCreateFlow::test_flow_has_logger_with_informative_name tests/core/test_flow.py::TestCreateFlow::test_create_flow_with_result tests/core/test_flow.py::TestCreateFlow::test_create_flow_with_storage tests/core/test_flow.py::TestCreateFlow::test_create_flow_with_storage_and_result tests/core/test_flow.py::TestCreateFlow::test_create_flow_with_environment tests/core/test_flow.py::TestCreateFlow::test_create_flow_auto_generates_tasks tests/core/test_flow.py::test_add_task_to_flow tests/core/test_flow.py::test_add_task_returns_task tests/core/test_flow.py::test_add_task_raise_an_error_if_the_task_is_not_a_task_class tests/core/test_flow.py::test_set_dependencies_adds_all_arguments_to_flow tests/core/test_flow.py::test_set_dependencies_converts_unkeyed_arguments_to_tasks tests/core/test_flow.py::test_set_dependencies_with_nested_ordered_constants_creates_a_single_constant[val0] tests/core/test_flow.py::test_set_dependencies_with_nested_ordered_constants_creates_a_single_constant[val1] tests/core/test_flow.py::test_set_dependencies_with_nested_ordered_constants_creates_a_single_constant[val2] tests/core/test_flow.py::test_set_dependencies_with_nested_ordered_constants_creates_a_single_constant[val3] tests/core/test_flow.py::test_set_dependencies_creates_mapped_edges tests/core/test_flow.py::test_set_dependencies_respects_unmapped tests/core/test_flow.py::test_binding_a_task_in_context_adds_it_to_flow tests/core/test_flow.py::test_binding_a_task_adds_it_to_flow tests/core/test_flow.py::test_binding_a_task_no_with_flow_raises_error tests/core/test_flow.py::test_adding_a_task_to_a_flow_twice_is_ok tests/core/test_flow.py::test_binding_a_task_to_two_different_flows_is_ok tests/core/test_flow.py::test_binding_a_task_with_var_kwargs_expands_the_kwargs tests/core/test_flow.py::test_calling_a_task_without_context_returns_a_copy tests/core/test_flow.py::test_calling_a_task_returns_a_copy tests/core/test_flow.py::test_calling_a_slugged_task_in_different_flows_is_ok tests/core/test_flow.py::test_context_manager_is_properly_applied_to_tasks tests/core/test_flow.py::test_that_flow_adds_and_removes_itself_from_prefect_context tests/core/test_flow.py::test_add_edge tests/core/test_flow.py::test_add_edge_raise_error_for_downstream_parameter tests/core/test_flow.py::test_add_edge_raise_error_for_duplicate_key_if_validate tests/core/test_flow.py::test_add_edge_returns_edge tests/core/test_flow.py::test_add_edge_from_contant tests/core/test_flow.py::test_chain tests/core/test_flow.py::test_splatting_chain_works_in_flow_context_without_duplication tests/core/test_flow.py::test_chain_works_in_flow_context_without_duplication tests/core/test_flow.py::test_iter tests/core/test_flow.py::test_detect_cycle tests/core/test_flow.py::test_eager_cycle_detection_defaults_false tests/core/test_flow.py::test_direct_cycles_are_always_detected_1 tests/core/test_flow.py::test_direct_cycles_are_always_detected_2 tests/core/test_flow.py::test_eager_validation_is_off_by_default tests/core/test_flow.py::test_eager_cycle_detection_works tests/core/test_flow.py::test_copy_handles_constants tests/core/test_flow.py::test_copy tests/core/test_flow.py::test_infer_root_tasks tests/core/test_flow.py::test_infer_terminal_tasks tests/core/test_flow.py::test_reference_tasks_are_terminal_tasks_by_default tests/core/test_flow.py::test_set_reference_tasks tests/core/test_flow.py::test_set_reference_tasks_at_init_with_empty_flow_raises_error tests/core/test_flow.py::test_set_reference_tasks_at_init tests/core/test_flow.py::test_reset_reference_tasks_to_terminal_tasks tests/core/test_flow.py::test_key_states_raises_error_if_not_part_of_flow tests/core/test_flow.py::test_key_states_raises_error_if_not_iterable tests/core/test_flow.py::test_warning_raised_if_tasks_are_created_but_not_added_to_flow tests/core/test_flow.py::test_warning_raised_if_tasks_are_copied_but_not_added_to_flow tests/core/test_flow.py::test_warning_raised_for_tasks_defined_in_flow_context_and_unused tests/core/test_flow.py::test_warning_raised_for_lambda_tasks_defined_in_flow_context_and_unused tests/core/test_flow.py::test_context_is_scoped_to_flow_context tests/core/test_flow.py::TestEquality::test_equality_based_on_tasks tests/core/test_flow.py::TestEquality::test_object_inequality tests/core/test_flow.py::TestEquality::test_equality_based_on_edges tests/core/test_flow.py::TestEquality::test_equality_based_on_name tests/core/test_flow.py::TestEquality::test_equality_based_on_reference_tasks tests/core/test_flow.py::test_update tests/core/test_flow.py::test_update_with_constants tests/core/test_flow.py::test_update_with_mapped_edges tests/core/test_flow.py::test_update_with_parameter_merge tests/core/test_flow.py::test_upstream_and_downstream_error_msgs_when_task_is_not_in_flow tests/core/test_flow.py::test_sorted_tasks tests/core/test_flow.py::test_sorted_tasks_with_ambiguous_sort tests/core/test_flow.py::test_sorted_tasks_with_start_task tests/core/test_flow.py::test_sorted_tasks_with_invalid_start_task tests/core/test_flow.py::test_flow_raises_for_irrelevant_user_provided_parameters tests/core/test_flow.py::test_flow_raises_for_missing_required_parameters tests/core/test_flow.py::test_flow_doesnt_raises_for_missing_nonrequired_parameters tests/core/test_flow.py::test_flow_accepts_unserializeable_parameters tests/core/test_flow.py::test_parameters_can_not_be_downstream_dependencies tests/core/test_flow.py::test_validate_cycles tests/core/test_flow.py::test_validate_missing_edge_downstream_tasks tests/core/test_flow.py::test_validate_missing_edge_upstream_tasks tests/core/test_flow.py::test_validate_missing_reference_tasks tests/core/test_flow.py::test_validate_edges_kwarg tests/core/test_flow.py::test_validate_edges tests/core/test_flow.py::test_skip_validate_edges tests/core/test_flow.py::test_skip_validation_in_init_with_kwarg tests/core/test_flow.py::TestCache::test_cache_created tests/core/test_flow.py::TestCache::test_cache_sorted_tasks tests/core/test_flow.py::TestCache::test_cache_sorted_tasks_with_args tests/core/test_flow.py::TestCache::test_cache_root_tasks tests/core/test_flow.py::TestCache::test_cache_terminal_tasks tests/core/test_flow.py::TestCache::test_cache_all_upstream_edges tests/core/test_flow.py::TestCache::test_cache_all_downstream_edges tests/core/test_flow.py::TestCache::test_cache_survives_pickling tests/core/test_flow.py::TestCache::test_adding_task_clears_cache tests/core/test_flow.py::TestCache::test_adding_edge_clears_cache tests/core/test_flow.py::TestCache::test_setting_reference_tasks_clears_cache tests/core/test_flow.py::TestReplace::test_replace_replaces_all_the_things tests/core/test_flow.py::TestReplace::test_replace_leaves_unset_reference_tasks_alone tests/core/test_flow.py::TestReplace::test_replace_update_slugs tests/core/test_flow.py::TestReplace::test_replace_complains_about_tasks_not_in_flow tests/core/test_flow.py::TestReplace::test_replace_runs_smoothly tests/core/test_flow.py::TestReplace::test_replace_converts_new_to_task tests/core/test_flow.py::TestReplace::test_replace_converts_new_collections_to_tasks tests/core/test_flow.py::TestGetTasks::test_get_tasks_defaults_to_return_everything tests/core/test_flow.py::TestGetTasks::test_get_tasks_defaults_to_name tests/core/test_flow.py::TestGetTasks::test_get_tasks_takes_intersection tests/core/test_flow.py::TestGetTasks::test_get_tasks_accepts_tags_and_requires_all_tags tests/core/test_flow.py::TestGetTasks::test_get_tasks_can_check_types tests/core/test_flow.py::TestSerialize::test_serialization tests/core/test_flow.py::TestSerialize::test_deserialization tests/core/test_flow.py::TestSerialize::test_serialize_validates_invalid_flows tests/core/test_flow.py::TestSerialize::test_serialize_includes_storage tests/core/test_flow.py::TestSerialize::test_serialize_adds_flow_to_storage_if_build tests/core/test_flow.py::TestSerialize::test_serialize_can_be_called_twice tests/core/test_flow.py::TestSerialize::test_serialize_fails_with_no_storage tests/core/test_flow.py::TestSerializedHash::test_is_same_with_same_flow tests/core/test_flow.py::TestSerializedHash::test_is_same_with_copied_flow tests/core/test_flow.py::TestSerializedHash::test_is_consistent_after_storage_build tests/core/test_flow.py::TestSerializedHash::test_is_different_before_and_after_storage_build tests/core/test_flow.py::TestSerializedHash::test_is_different_with_different_flow_name tests/core/test_flow.py::TestSerializedHash::test_is_same_in_new_python_instance tests/core/test_flow.py::TestSerializedHash::test_task_order_is_deterministic tests/core/test_flow.py::TestSerializedHash::test_parameter_order_is_deterministic tests/core/test_flow.py::TestSerializedHash::test_is_different_with_modified_flow_name tests/core/test_flow.py::TestSerializedHash::test_is_different_with_modified_flow_storage tests/core/test_flow.py::TestSerializedHash::test_is_different_with_different_flow_tasks tests/core/test_flow.py::TestFlowRunMethod::test_flow_dot_run_runs_on_schedule tests/core/test_flow.py::TestFlowRunMethod::test_flow_dot_run_with_paused_states_hangs tests/core/test_flow.py::TestFlowRunMethod::test_flow_dot_run_passes_scheduled_parameters tests/core/test_flow.py::TestFlowRunMethod::test_flow_dot_run_doesnt_persist_stale_scheduled_params tests/core/test_flow.py::TestFlowRunMethod::test_flow_dot_run_doesnt_run_on_schedule tests/core/test_flow.py::TestFlowRunMethod::test_flow_dot_run_returns_tasks_when_running_off_schedule tests/core/test_flow.py::TestFlowRunMethod::test_flow_dot_run_responds_to_config tests/core/test_flow.py::TestFlowRunMethod::test_flow_dot_run_stops_on_schedule tests/core/test_flow.py::TestFlowRunMethod::test_flow_dot_run_schedule_continues_on_executor_failure tests/core/test_flow.py::TestFlowRunMethod::test_scheduled_runs_handle_retries tests/core/test_flow.py::TestFlowRunMethod::test_flow_dot_run_handles_cached_states tests/core/test_flow.py::TestFlowRunMethod::test_flow_dot_run_handles_cached_states_across_runs tests/core/test_flow.py::TestFlowRunMethod::test_flow_dot_run_without_schedule_can_run_cached_tasks tests/core/test_flow.py::TestFlowRunMethod::test_flow_dot_run_handles_mapped_cached_states tests/core/test_flow.py::TestFlowRunMethod::test_flow_dot_run_handles_cached_states_across_runs_with_always_run_trigger tests/core/test_flow.py::TestFlowRunMethod::test_flow_dot_run_handles_mapped_cached_states_across_runs tests/core/test_flow.py::TestFlowRunMethod::test_flow_dot_run_handles_mapped_cached_states_with_differing_lengths tests/core/test_flow.py::TestFlowRunMethod::test_flow_dot_run_handles_mapped_cached_states_with_non_cached tests/core/test_flow.py::TestFlowRunMethod::test_scheduled_runs_handle_mapped_retries tests/core/test_flow.py::TestFlowRunMethod::test_flow_run_accepts_state_kwarg tests/core/test_flow.py::TestFlowRunMethod::test_flow_dot_run_sets_scheduled_start_time tests/core/test_flow.py::TestFlowRunMethod::test_flow_dot_run_does_not_set_scheduled_start_time_globally tests/core/test_flow.py::TestFlowRunMethod::test_flow_dot_run_persists_scheduled_start_time_across_retries tests/core/test_flow.py::TestFlowRunMethod::test_flow_dot_run_updates_the_scheduled_start_time_of_each_scheduled_run tests/core/test_flow.py::TestFlowDiagnostics::test_flow_diagnostics tests/core/test_flow.py::TestFlowRegister::test_flow_register_uses_default_storage[prefect.storage.Docker] tests/core/test_flow.py::TestFlowRegister::test_flow_register_uses_default_storage[prefect.storage.Local] tests/core/test_flow.py::TestFlowRegister::test_flow_register_passes_kwargs_to_storage tests/core/test_flow.py::TestFlowRegister::test_flow_register_sets_universal_run_if_empty tests/core/test_flow.py::TestFlowRegister::test_flow_register_auto_labels_if_labeled_storage_used[storage0-environment] tests/core/test_flow.py::TestFlowRegister::test_flow_register_auto_labels_if_labeled_storage_used[storage0-run_config] tests/core/test_flow.py::TestFlowRegister::test_flow_register_auto_labels_if_labeled_storage_used[storage1-environment] tests/core/test_flow.py::TestFlowRegister::test_flow_register_auto_labels_if_labeled_storage_used[storage1-run_config] tests/core/test_flow.py::TestFlowRegister::test_flow_register_auto_labels_if_labeled_storage_used[storage2-environment] tests/core/test_flow.py::TestFlowRegister::test_flow_register_auto_labels_if_labeled_storage_used[storage2-run_config] tests/core/test_flow.py::TestFlowRegister::test_flow_register_auto_labels_if_labeled_storage_used[storage3-environment] tests/core/test_flow.py::TestFlowRegister::test_flow_register_auto_labels_if_labeled_storage_used[storage3-run_config] tests/core/test_flow.py::TestFlowRegister::test_flow_register_auto_sets_result_if_storage_has_default[storage0] tests/core/test_flow.py::TestFlowRegister::test_flow_register_auto_sets_result_if_storage_has_default[storage1] tests/core/test_flow.py::TestFlowRegister::test_flow_register_auto_sets_result_if_storage_has_default[storage2] tests/core/test_flow.py::TestFlowRegister::test_flow_register_auto_sets_result_if_storage_has_default[storage3] tests/core/test_flow.py::TestFlowRegister::test_flow_register_doesnt_override_custom_set_result tests/core/test_flow.py::TestFlowRegister::test_flow_register_doesnt_overwrite_labels_if_local_storage_is_used tests/core/test_flow.py::TestFlowRegister::test_flow_register_errors_if_in_flow_context tests/core/test_flow.py::TestFlowRegister::test_flow_register_warns_if_mixing_environment_and_executor tests/core/test_flow.py::test_bad_flow_runner_code_still_returns_state_obj tests/core/test_flow.py::test_flow_run_raises_informative_error_for_certain_kwargs tests/core/test_flow.py::test_flow_run_raises_if_no_more_scheduled_runs tests/core/test_flow.py::test_flow_run_respects_state_kwarg tests/core/test_flow.py::test_flow_run_respects_task_state_kwarg tests/core/test_flow.py::test_flow_run_handles_error_states_when_initial_state_is_provided tests/core/test_flow.py::test_looping_works_in_a_flow tests/core/test_flow.py::test_pause_resume_works_with_retries tests/core/test_flow.py::test_looping_with_retries_works_in_a_flow tests/core/test_flow.py::test_looping_with_retries_resets_run_count tests/core/test_flow.py::test_starting_at_arbitrary_loop_index tests/core/test_flow.py::test_flow_run_name_as_run_param tests/core/test_flow.py::TestSaveLoad::test_save_saves_and_load_loads tests/core/test_flow.py::TestSaveLoad::test_save_saves_has_a_default tests/core/test_flow.py::TestSaveLoad::test_load_accepts_name_and_sluggified_name tests/core/test_flow.py::test_timeout_actually_stops_execution[local] tests/core/test_flow.py::test_timeout_actually_stops_execution[sync] tests/core/test_flow.py::test_timeout_actually_stops_execution[mthread] tests/core/test_flow.py::test_timeout_actually_stops_execution[mproc_local] tests/core/test_flow.py::test_timeout_actually_stops_execution[mproc] tests/core/test_flow.py::test_results_write_to_formatted_locations tests/core/test_flow.py::test_results_write_to_custom_formatters tests/core/test_flow.py::test_run_agent_passes_flow_labels[environment] tests/core/test_flow.py::test_run_agent_passes_flow_labels[run_config] tests/core/test_flow.py::TestSlugGeneration::test_slugs_are_stable tests/core/test_flow.py::TestSlugGeneration::test_slugs_incorporate_tags_and_order