Ensure resumed CI jobs return to the correct runner
MR: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/231517+s ## Why are we doing this work When a CI job is suspended (via the suspendable environments feature), the runner preserves the cloud VM state and emits a resume key encoding which runner holds the environment. When the next job is dispatched to resume that environment, **any runner matching the job's tags can pick it up** — but only the original runner's taskscaler knows about the suspended acquisition. A wrong runner receiving the resume job fails immediately with an unknown acquisition key. Rails needs to enforce the coupling: a resumed job must return to exactly the runner that suspended it. ## Relevant links - Epic: [gitlab-org#21159 — Resumable Jobs for CI and Agent Sessions](https://gitlab.com/groups/gitlab-org/-/work_items/21159) - Blueprint: [Suspendable Environments](https://gitlab.com/gitlab-com/content-sites/handbook/-/merge_requests/18954) ## Implementation plan - [x] Add `Gitlab::Ci::Matching::ResumeKey` — parses `environment_key`, mirrors Go's `helpers.ShortenToken` (strip known prefixes, take first 9 chars), `matches_runner?` returns true only if the runner's shortened token matches the key's runner ID prefix - `lib/gitlab/ci/matching/resume_key.rb` - `spec/lib/gitlab/ci/matching/resume_key_spec.rb` (uses `fast_spec_helper`) - [x] Wire into `RegisterJobService#runner_matched?` — add `resume_environment_available_to_runner?` which reads `build.options[:environment_key]`, delegates to `ResumeKey#matches_runner?`, and logs the routing decision to `application_json.log` - `app/services/ci/register_job_service.rb` - `spec/services/ci/register_job_service_spec.rb` ## Verification steps 1. Start two runners. 2. Create a suspend workload via `RunWorkloadService` with `suspend_on_success: true` 3. Wait for the job to complete and the runner to log the resume key 4. Create a resume workload with that key as `environment_key` and a tag only the correct runner has 5. Put a non-matching tag on the runner that ran the job earlier to prevent it from picking up the job again. 6. Observe that the other runner cannot pick the job either because the environment key does not match.
issue