From 50406fd59250f9bfa8705e6eafde7aba10a51776 Mon Sep 17 00:00:00 2001 From: Anthony Volk <14987227+anth-volk@users.noreply.github.com> Date: Tue, 20 Oct 2026 01:07:30 +0400 Subject: [PATCH 1/3] Run the Tests workflow for pull requests into any base branch The pull_request trigger was filtered to branches: [main]. A stacked pull request targets another pull request's branch, so GitHub started no Tests run for it at all. The filter is removed. Pushes still run the workflow only for main. Nothing else in the workflow depends on the base branch. The concurrency group is keyed on the pull request number, and country selection reads the pull request's own changed files from the API. Pull requests from this repository's branches get the same token permissions and secrets whatever their base. New tests read the workflow's triggers. Pull requests into main, into another pull request's branch and into a nested branch name must all run it, and pushes must run it only for main. The agent guide and a changelog fragment say so. Fixes #1321 Co-Authored-By: Claude Opus 5.5 --- .github/workflows/test.yml | 3 ++- ...-ci-pull-requests-into-any-base.changed.md | 1 + docs/agent-guide.md | 4 ++- .../shared/test_ci_integration_job.py | 26 +++++++++++++++++++ 4 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 changelog.d/1321-ci-pull-requests-into-any-base.changed.md diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1e7898bfb3..e5a2a415cd 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -3,8 +3,9 @@ name: Tests on: push: branches: [main] + # Pull requests into any base branch: a stacked pull request targets + # another pull request's branch and needs the same jobs. pull_request: - branches: [main] # Cancel outdated PR runs; give each main push its own independent group. concurrency: diff --git a/changelog.d/1321-ci-pull-requests-into-any-base.changed.md b/changelog.d/1321-ci-pull-requests-into-any-base.changed.md new file mode 100644 index 0000000000..40534bcbc0 --- /dev/null +++ b/changelog.d/1321-ci-pull-requests-into-any-base.changed.md @@ -0,0 +1 @@ +The Tests workflow (`.github/workflows/test.yml`) now runs for pull requests into any base branch, not only `main` (microcosm#1321). A stacked pull request, whose base is another pull request's branch, previously got no CI at all. Pushes still run it only for `main`, and the jobs, the country selection, the concurrency group and the secrets are unchanged. diff --git a/docs/agent-guide.md b/docs/agent-guide.md index b14084800d..e98d73e8aa 100644 --- a/docs/agent-guide.md +++ b/docs/agent-guide.md @@ -41,7 +41,9 @@ or `uv sync --all-packages --all-extras`. To keep both countries set up, point PR CI (`.github/workflows/test.yml`) has `lint`, `engine-free`, `engine-us`, `engine-uk`, `integration-uk`, and `wheels` jobs, plus the -`select-countries` orchestration job. +`select-countries` orchestration job. It runs for pull requests into any base +branch, so a stacked pull request, whose base is another pull request's +branch, gets the same jobs; pushes run it only for `main`. `tools/ci_test_plan.py` is the only authority for test-directory ownership, CI job assignment, country ownership, US timing-report categories, and changed-path country selection. Its `TEST_GROUPS` registry defines every valid diff --git a/packages/microcosm-build/tests/engine_free/shared/test_ci_integration_job.py b/packages/microcosm-build/tests/engine_free/shared/test_ci_integration_job.py index bcaac635a3..7dc8e1b0a7 100644 --- a/packages/microcosm-build/tests/engine_free/shared/test_ci_integration_job.py +++ b/packages/microcosm-build/tests/engine_free/shared/test_ci_integration_job.py @@ -2,6 +2,7 @@ from __future__ import annotations +import pytest import yaml from test_support.paths import paths_for @@ -71,6 +72,31 @@ def test_integration_job_is_uk_selected_and_read_only() -> None: assert "run: bash tools/run_integration_tests.sh" in integration +def _workflow_triggers() -> dict: + workflow = yaml.safe_load(_TEST_WORKFLOW.read_text(encoding="utf-8")) + # PyYAML follows YAML 1.1, which reads the bare key ``on`` as boolean true. + return workflow[True] + + +@pytest.mark.parametrize( + "base_branch", + ["main", "orrery-projection-without-store", "stacked/parent-branch"], +) +def test_pull_requests_into_any_base_branch_run_the_tests_workflow( + base_branch: str, +) -> None: + """A stacked pull request targets another pull request's branch, not main, + and must get the same jobs.""" + branch_filters = _workflow_triggers()["pull_request"] or {} + + assert base_branch in branch_filters.get("branches", [base_branch]) + assert base_branch not in branch_filters.get("branches-ignore", []) + + +def test_only_pushes_to_main_run_the_tests_workflow() -> None: + assert _workflow_triggers()["push"] == {"branches": ["main"]} + + def test_every_workflow_job_caps_its_runtime() -> None: """A hung job must not hold an org-shared runner for GitHub's 6-hour default.""" jobs = yaml.safe_load(_TEST_WORKFLOW.read_text(encoding="utf-8"))["jobs"] From b701a5e4937bed6a39b54514f0021a10afa393c0 Mon Sep 17 00:00:00 2001 From: Anthony Volk <14987227+anth-volk@users.noreply.github.com> Date: Tue, 20 Oct 2026 16:48:32 +0400 Subject: [PATCH 2/3] Assert the pull_request trigger has no base-branch filter The previous test checked three literal branch names against the trigger's branches and branches-ignore lists. GitHub matches those entries as glob patterns, so a membership check proves nothing about other bases: a filter listing exactly those three names passed it, although pull requests into any other base would get no run, and a filter of '**', which runs for every base, failed it. The intended design is no base-branch filter at all, so the test now asserts that the pull_request trigger has neither a branches nor a branches-ignore key. _workflow_triggers is typed as dict[str, Any]. Co-Authored-By: Claude Opus 5.5 --- .../shared/test_ci_integration_job.py | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/microcosm-build/tests/engine_free/shared/test_ci_integration_job.py b/packages/microcosm-build/tests/engine_free/shared/test_ci_integration_job.py index 7dc8e1b0a7..70caa25491 100644 --- a/packages/microcosm-build/tests/engine_free/shared/test_ci_integration_job.py +++ b/packages/microcosm-build/tests/engine_free/shared/test_ci_integration_job.py @@ -2,7 +2,8 @@ from __future__ import annotations -import pytest +from typing import Any + import yaml from test_support.paths import paths_for @@ -72,25 +73,24 @@ def test_integration_job_is_uk_selected_and_read_only() -> None: assert "run: bash tools/run_integration_tests.sh" in integration -def _workflow_triggers() -> dict: +def _workflow_triggers() -> dict[str, Any]: workflow = yaml.safe_load(_TEST_WORKFLOW.read_text(encoding="utf-8")) # PyYAML follows YAML 1.1, which reads the bare key ``on`` as boolean true. return workflow[True] -@pytest.mark.parametrize( - "base_branch", - ["main", "orrery-projection-without-store", "stacked/parent-branch"], -) -def test_pull_requests_into_any_base_branch_run_the_tests_workflow( - base_branch: str, -) -> None: +def test_pull_requests_into_any_base_branch_run_the_tests_workflow() -> None: """A stacked pull request targets another pull request's branch, not main, - and must get the same jobs.""" - branch_filters = _workflow_triggers()["pull_request"] or {} + and must get the same jobs. + + GitHub matches ``branches`` and ``branches-ignore`` entries as glob + patterns, so checking particular branch names against them proves nothing + about other bases. The trigger must have no base-branch filter at all. + """ + pull_request_trigger = _workflow_triggers()["pull_request"] or {} - assert base_branch in branch_filters.get("branches", [base_branch]) - assert base_branch not in branch_filters.get("branches-ignore", []) + base_branch_filters = {"branches", "branches-ignore"} & pull_request_trigger.keys() + assert sorted(base_branch_filters) == [] def test_only_pushes_to_main_run_the_tests_workflow() -> None: From 81650f002d2525b544ac058095371e1db439da2e Mon Sep 17 00:00:00 2001 From: Anthony Volk <14987227+anth-volk@users.noreply.github.com> Date: Tue, 20 Oct 2026 16:48:32 +0400 Subject: [PATCH 3/3] Note the runner cost of running CI for pull requests into any base Every push to a pull request's branch now runs the full Tests workflow on the organization's shared runners, whatever the base, so merging one change up a stack of N pull requests starts N full runs. The agent guide says so. Co-Authored-By: Claude Opus 5.5 --- docs/agent-guide.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/agent-guide.md b/docs/agent-guide.md index e98d73e8aa..b6b043db82 100644 --- a/docs/agent-guide.md +++ b/docs/agent-guide.md @@ -43,7 +43,11 @@ PR CI (`.github/workflows/test.yml`) has `lint`, `engine-free`, `engine-us`, `engine-uk`, `integration-uk`, and `wheels` jobs, plus the `select-countries` orchestration job. It runs for pull requests into any base branch, so a stacked pull request, whose base is another pull request's -branch, gets the same jobs; pushes run it only for `main`. +branch, gets the same jobs; pushes run it only for `main`. That costs runner +time: every push to a pull request's branch, whatever its base, runs the full +workflow on the organization's shared runners, with the jobs and country +selection a pull request into `main` would get. Merging one change up a stack +of N pull requests therefore starts N full runs. `tools/ci_test_plan.py` is the only authority for test-directory ownership, CI job assignment, country ownership, US timing-report categories, and changed-path country selection. Its `TEST_GROUPS` registry defines every valid