diff --git a/.github/workflows/strix.yml b/.github/workflows/strix.yml index ef0966cf4d..51c56b381c 100644 --- a/.github/workflows/strix.yml +++ b/.github/workflows/strix.yml @@ -130,7 +130,7 @@ jobs: # docs/doctoring/required-workflow-path-filter-boundary.md. # Fails OPEN: an unreadable, empty, or truncated file list scans everything. if: github.event_name != 'pull_request_target' || (github.event.action != 'closed' && github.event.action != 'converted_to_draft') - runs-on: ubuntu-24.04 + runs-on: ${{ (github.repository == 'ContextualWisdomLab/.github' || github.repository == 'ContextualWisdomLab/fast-mlsirm') && github.workflow_ref == 'ContextualWisdomLab/.github/.github/workflows/strix.yml@refs/heads/main' && fromJSON('{"group":"CWL central control","labels":["self-hosted","linux","x64","cwlab-control"]}') || fromJSON('["ubuntu-24.04"]') }} timeout-minutes: 5 permissions: contents: read @@ -187,7 +187,7 @@ jobs: if: >- github.event_name != 'pull_request_target' || (github.event.action != 'closed' && github.event.action != 'converted_to_draft') - runs-on: ubuntu-24.04 + runs-on: ${{ (github.repository == 'ContextualWisdomLab/.github' || github.repository == 'ContextualWisdomLab/fast-mlsirm') && github.workflow_ref == 'ContextualWisdomLab/.github/.github/workflows/strix.yml@refs/heads/main' && fromJSON('{"group":"CWL central control","labels":["self-hosted","linux","x64","cwlab-control"]}') || fromJSON('["ubuntu-24.04"]') }} timeout-minutes: 5 permissions: contents: read @@ -260,7 +260,7 @@ jobs: github.event.pull_request.base.repo.full_name || github.repository }}-${{ github.event.pull_request.number || github.run_id }} cancel-in-progress: true - runs-on: ubuntu-24.04 + runs-on: ${{ (github.repository == 'ContextualWisdomLab/.github' || github.repository == 'ContextualWisdomLab/fast-mlsirm') && github.workflow_ref == 'ContextualWisdomLab/.github/.github/workflows/strix.yml@refs/heads/main' && fromJSON('{"group":"CWL central control","labels":["self-hosted","linux","x64","cwlab-control"]}') || fromJSON('["ubuntu-24.04"]') }} # Bound this gh-api-only cleanup job so a stuck call (rate limit, hung # `gh api --paginate`) cannot silently occupy a runner for GitHub's # 360-minute platform default -- exactly the window when a busy PR is @@ -1172,7 +1172,7 @@ jobs: name: publish-manual-pr-evidence-status needs: strix if: ${{ always() && !cancelled() && github.event_name == 'repository_dispatch' && github.event.client_payload.pr_head_sha != '' }} - runs-on: ubuntu-24.04 + runs-on: ${{ (github.repository == 'ContextualWisdomLab/.github' || github.repository == 'ContextualWisdomLab/fast-mlsirm') && github.workflow_ref == 'ContextualWisdomLab/.github/.github/workflows/strix.yml@refs/heads/main' && fromJSON('{"group":"CWL central control","labels":["self-hosted","linux","x64","cwlab-control"]}') || fromJSON('["ubuntu-24.04"]') }} # Single-shot OIDC exchange plus a handful of curl/gh api calls, no loop # or pagination -- same shape as the agent-mention-*-dispatch.yml # validate-and-forward jobs, which bound at timeout-minutes: 5. Without diff --git a/docs/doctoring/central-dedicated-runner-routing-20260927.md b/docs/doctoring/central-dedicated-runner-routing-20260927.md index c79175ac7d..aa84465589 100644 --- a/docs/doctoring/central-dedicated-runner-routing-20260927.md +++ b/docs/doctoring/central-dedicated-runner-routing-20260927.md @@ -109,3 +109,25 @@ capacity or independent service-time measurement justifies another solver. The routing regression fails against the unchanged baseline. Workflow syntax and affected contracts passed: 238 passed, 2 skipped with `GITHUB_ACTIONS=true`; `actionlint` and `git diff --check` passed. + + +## fast-mlsirm Strix control admission + +Current fast-mlsirm PR #2220 head `4eaeb799a6647ea29f3f4902d9ca79a1377e795c` +queued Strix admission job `108617323217` with `ubuntu-24.04`, despite the +self-hosted rollout. Route only changed-scope, current-head admission, +superseded-run cleanup and manual status publication through group 6 when +the source is exactly central `strix.yml@refs/heads/main` and the caller is +the central repository or fast-mlsirm. These jobs do not check out PR code. +The model scan keeps its existing hosted image and all evidence, credentials, +fork handling and live-head validation remain intact. + +Reuse the deployed allocation; no new service-time or capacity measurement +justifies a different solver result. Deployment requires adding only central +`strix.yml@refs/heads/main` to group 6's selected workflows, preserving all +existing restrictions and grants. Old queued jobs keep their original source. + +The routing test failed on the unmodified workflow. The affected runner, +changed-scope and dependency-hash tests passed (21 tests); actionlint and +diff whitespace checks passed. This is local source proof, not completed +consumer gate evidence. diff --git a/tests/test_docs_only_pr_runner_admission.py b/tests/test_docs_only_pr_runner_admission.py index 6a1908e6f8..a0f691e2a2 100644 --- a/tests/test_docs_only_pr_runner_admission.py +++ b/tests/test_docs_only_pr_runner_admission.py @@ -93,12 +93,12 @@ def test_gate_job_is_byte_identical_across_the_five_workflows_apart_from_if(): workflow = _read(filename) block = _top_level_job_block(workflow, "changed-scope") normalized = "\n".join( - line for line in block.splitlines() if not line.strip().startswith("if:") + line for line in block.splitlines() if not line.strip().startswith(("if:", "runs-on:")) ) normalized_blocks.add(normalized) assert len(normalized_blocks) == 1, ( "changed-scope gate copies drifted; keep them byte-identical apart " - "from the single 'if:' line" + "from the event guard and separately tested runner allocation" ) @@ -132,9 +132,9 @@ def test_gate_jobs_use_supported_runner_allocation(): """Scope jobs preserve trusted-main routing and a supported hosted fallback.""" for filename in GATE_WORKFLOWS: block = _top_level_job_block(_read(filename), "changed-scope") - if filename == "opencode-review.yml": + if filename in ("opencode-review.yml", "strix.yml"): assert '"group":"CWL central control"' in block, filename - assert "github.workflow_ref == 'ContextualWisdomLab/.github/.github/workflows/opencode-review.yml@refs/heads/main'" in block, filename + assert f"github.workflow_ref == 'ContextualWisdomLab/.github/.github/workflows/{filename}@refs/heads/main'" in block, filename assert "fromJSON('[\"ubuntu-24.04\"]')" in block, filename elif filename == "noema-review.yml": assert "endsWith(github.workflow_ref, '@refs/heads/main')" in block, filename diff --git a/tests/test_required_review_runner_image_contract.py b/tests/test_required_review_runner_image_contract.py index 991c97d703..4a800219b2 100644 --- a/tests/test_required_review_runner_image_contract.py +++ b/tests/test_required_review_runner_image_contract.py @@ -27,8 +27,20 @@ def assert_explicit_supported_image(self, path: Path) -> None: self.assertEqual(runs_on, {"runs-on: ubuntu-24.04"}) def test_strix_uses_explicit_supported_image(self) -> None: - """Require every Strix job to use explicit Ubuntu 24.04.""" - self.assert_explicit_supported_image(STRIX) + """Route trusted metadata to control while preserving the scan image.""" + workflow = STRIX.read_text(encoding="utf-8") + for name in ("changed-scope", "admit-current-head", "cancel-superseded-pr-runs", "publish-manual-pr-evidence-status"): + block = re.split(r"\n [a-z][a-z-]*:\n", workflow.split(f"\n {name}:\n", 1)[1], maxsplit=1)[0] + self.assertIn('"group":"CWL central control"', block) + self.assertIn('"labels":["self-hosted","linux","x64","cwlab-control"]', block) + self.assertIn("github.workflow_ref == 'ContextualWisdomLab/.github/.github/workflows/strix.yml@refs/heads/main'", block) + self.assertIn("github.repository == 'ContextualWisdomLab/.github'", block) + self.assertIn("github.repository == 'ContextualWisdomLab/fast-mlsirm'", block) + self.assertIn("fromJSON('[\"ubuntu-24.04\"]')", block) + self.assertNotIn("actions/checkout", block) + scan = workflow.split("\n strix:\n", 1)[1].split("\n publish-manual-pr-evidence-status:\n", 1)[0] + self.assertIn("runs-on: ubuntu-24.04", scan) + self.assertNotIn("cwlab-control", scan) def test_opencode_review_uses_explicit_supported_image(self) -> None: """Keep metadata-only OpenCode admission on the trusted control pool."""