Investigate and address stale `vulnerability_reads` ES docs during group transfer + `task: :all` consolidation
## Background
[!229223](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/229223) adds `Search::Elastic::Delete::VulnerabilityReadsService` and wires it into the group transfer flow via `delete_vulnerability_reads_with_old_routing`. During review, [@bala.kumar](https://gitlab.com/bala.kumar) asked whether we could simplify the group transfer cleanup by calling `task: :all` (which fans out to all `PROJECT_TASKS`) instead of scheduling `delete_project_vulnerabilities` and `delete_project_vulnerability_reads` separately.
[@terrichu](https://gitlab.com/terrichu) identified two blockers:
1. **Migration guard**: `delete_vulnerability_reads_with_old_routing` is gated on `Elastic::DataMigrationService.migration_has_finished?(:create_vulnerability_reads_index)`. `task: :all` in `DeleteWorker` fans out to all `PROJECT_TASKS` without any per-task migration guards, so we can't consolidate until `DeleteWorker` supports conditional task execution.
2. **Potential data bug**: `ElasticGroupAssociationDeletionWorker` (called via `process_group_associations`) handles group-level work items using `group_<root_ancestor_id>` routing. However, it only targets `namespace_id` — meaning **project-level work items** (which also use `group_<root_ancestor_id>` routing) may **not** get stale document cleanup during a group transfer. This needs manual verification.
## Potential Bug: Project-level Work Items Not Cleaned Up on Group Transfer
During a group transfer, `process_group_associations` calls `ElasticGroupAssociationDeletionWorker` which runs a `delete_by_query` on the `WorkItem` index filtered by `namespace_id` (group IDs) with the old root ancestor's routing key. This correctly handles group-scoped work items (epics, group issues).
However, **project-level work items** are indexed with `group_<root_ancestor_id>` routing and have a `project_id` field. The `ElasticGroupAssociationDeletionWorker` filters only by `namespace_id` (group IDs), not `project_id`. After a group transfer, project-level work items under the transferred group may still exist in the old routing shard under the old `root_ancestor_id`, and the worker would not delete them because it only queries by `namespace_id` (which for project work items is the project namespace, not the group namespace).
`DeleteWorker` with `task: :delete_project_work_items` (`ProjectWorkItemsService`) does handle project-level work items correctly (filters by `project_id` + excludes new `traversal_id`), but it is **not called** during group transfer — only during project transfer (`EE::Projects::TransferService`).
### What needs verification
- [ ] Manually verify: after a group transfer, do project-level work items under the transferred group get stale docs cleaned up in the `WorkItem` ES index?
- [ ] Check whether `EE::Projects::TransferService` also calls `delete_project_work_items` and whether that covers the group transfer case transitively.
## Proposal
### Part 1: Verify and fix the potential bug (project-level work items on group transfer)
If confirmed, the fix is to also schedule `DeleteWorker` with `task: :delete_project_work_items` for each project during group transfer (similar to how `delete_vulnerability_reads_with_old_routing` is now called per-project). This should be done in `post_update_hooks` in `EE::Groups::TransferService`.
### Part 2: Support migration guards in `task: :all` (refactor)
To allow consolidation of the per-task scheduling calls, `DeleteWorker#run_all_project_tasks` should support skipping tasks that have a migration guard. Options:
- Add an optional `migration_guard` key to the `PROJECT_TASKS` hash that is checked before scheduling.
- Or gate the entire `task: :all` path on all required migrations having finished.
Once this is in place, the group transfer code can be simplified to a single `DeleteWorker.perform_async(task: :all, ...)` call per project.
## References
- [!229223](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/229223) — Add deletion service for vulnerability_reads ES index (parent MR)
- Original discussion: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/229223#note_3253898923
issue