From 99d90ed85f8b7c49d5b4898c8c7709342ef489b3 Mon Sep 17 00:00:00 2001 From: Brad Hilton Date: Mon, 28 Sep 2026 15:04:36 +0000 Subject: [PATCH 1/2] Fix same-head TrainerRank GPU reuse --- .github/workflows/trainer-rank-gpu.yml | 5 ++-- tests/unit/test_trainer_rank_gpu_workflow.py | 27 ++++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 tests/unit/test_trainer_rank_gpu_workflow.py diff --git a/.github/workflows/trainer-rank-gpu.yml b/.github/workflows/trainer-rank-gpu.yml index 0d208ad19..d7e56540f 100644 --- a/.github/workflows/trainer-rank-gpu.yml +++ b/.github/workflows/trainer-rank-gpu.yml @@ -48,9 +48,10 @@ jobs: }); const candidates = runs.data.workflow_runs.filter(run => run.id !== context.runId && + run.head_repository?.full_name === pull.head.repo.full_name && + run.head_branch === pull.head.ref && run.head_sha === pull.head.sha && - run.path === '.github/workflows/trainer-rank-gpu.yml' && - (run.pull_requests ?? []).some(previous => previous.number === pull.number) + run.path === '.github/workflows/trainer-rank-gpu.yml' ).slice(0, 10); for (const run of candidates) { const jobs = await github.rest.actions.listJobsForWorkflowRun({ diff --git a/tests/unit/test_trainer_rank_gpu_workflow.py b/tests/unit/test_trainer_rank_gpu_workflow.py new file mode 100644 index 000000000..9143e0cd1 --- /dev/null +++ b/tests/unit/test_trainer_rank_gpu_workflow.py @@ -0,0 +1,27 @@ +from pathlib import Path + + +def test_ready_for_review_reuse_has_stable_source_provenance() -> None: + workflow = ( + Path(__file__).parents[2] / ".github/workflows/trainer-rank-gpu.yml" + ).read_text() + + assert "github.event.action == 'ready_for_review'" in workflow + assert "run.head_repository?.full_name === pull.head.repo.full_name" in workflow + assert "run.head_branch === pull.head.ref" in workflow + assert "run.head_sha === pull.head.sha" in workflow + assert "run.path === '.github/workflows/trainer-rank-gpu.yml'" in workflow + assert "run.pull_requests" not in workflow + + +def test_ready_for_review_reuse_requires_exact_base_artifact_and_gpu_job() -> None: + workflow = ( + Path(__file__).parents[2] / ".github/workflows/trainer-rank-gpu.yml" + ).read_text() + + assert "job.name === 'Run on 2x H200' && job.conclusion === 'success'" in workflow + assert "!artifact.expired" in workflow + assert ( + "`trainer-rank-result-${run.id}-${run.run_attempt}-${pull.base.sha}`" + in workflow + ) From 1b23f83bb080909b54c8f45de8f8ad08166a6912 Mon Sep 17 00:00:00 2001 From: Brad Hilton Date: Mon, 28 Sep 2026 15:08:59 +0000 Subject: [PATCH 2/2] Fail closed on incomplete GPU reuse identity --- .github/workflows/trainer-rank-gpu.yml | 12 ++++++++++++ tests/unit/test_trainer_rank_gpu_workflow.py | 6 ++++++ 2 files changed, 18 insertions(+) diff --git a/.github/workflows/trainer-rank-gpu.yml b/.github/workflows/trainer-rank-gpu.yml index d7e56540f..0a331022d 100644 --- a/.github/workflows/trainer-rank-gpu.yml +++ b/.github/workflows/trainer-rank-gpu.yml @@ -38,6 +38,18 @@ jobs: with: script: | const pull = context.payload.pull_request; + const identity = [ + pull?.head?.repo?.full_name, + pull?.head?.ref, + pull?.head?.sha, + pull?.base?.sha, + ]; + if (!identity.every(value => + typeof value === 'string' && value.length > 0 + )) { + core.warning('Prior GPU validation identity is incomplete.'); + return; + } const runs = await github.rest.actions.listWorkflowRuns({ ...context.repo, workflow_id: 'trainer-rank-gpu.yml', diff --git a/tests/unit/test_trainer_rank_gpu_workflow.py b/tests/unit/test_trainer_rank_gpu_workflow.py index 9143e0cd1..52a54aa6b 100644 --- a/tests/unit/test_trainer_rank_gpu_workflow.py +++ b/tests/unit/test_trainer_rank_gpu_workflow.py @@ -7,6 +7,12 @@ def test_ready_for_review_reuse_has_stable_source_provenance() -> None: ).read_text() assert "github.event.action == 'ready_for_review'" in workflow + assert "pull?.head?.repo?.full_name" in workflow + assert "pull?.head?.ref" in workflow + assert "pull?.head?.sha" in workflow + assert "pull?.base?.sha" in workflow + assert "identity.every(value =>" in workflow + assert "typeof value === 'string' && value.length > 0" in workflow assert "run.head_repository?.full_name === pull.head.repo.full_name" in workflow assert "run.head_branch === pull.head.ref" in workflow assert "run.head_sha === pull.head.sha" in workflow