Skip to content

chore: change to centralized managed GitHub pool - #86

Open
tibrnui wants to merge 1 commit into
mainfrom
chore/migrate-runners-to-uipath-images-7a21e4d2
Open

chore: change to centralized managed GitHub pool#86
tibrnui wants to merge 1 commit into
mainfrom
chore/migrate-runners-to-uipath-images-7a21e4d2

Conversation

@tibrnui

@tibrnui tibrnui commented Aug 6, 2026

Copy link
Copy Markdown

Summary

Moves this repository's workflows to the centralized managed GitHub pool.

  • Runner images are prefixed with uipath- in all workflow files
  • e.g. ubuntu-latestuipath-ubuntu-latest

Changes

All workflow .yml files (including non-standard locations like workflows-src/) with static runs-on values are updated.
Dynamic expressions (${{ ... }}) and already-prefixed images are skipped.

Action Version Pinning

All uses: references are pinned to the SHA of the latest release published ≥ 48h ago.
This prevents supply-chain attacks via recently-published compromised versions.

8 workflow file(s) modified, 4 action(s) pinned
Runners migrated: ubuntu-latest ubuntu-24.04 ubuntu-22.04 ubuntu-24.04-arm ubuntu-22.04-arm ubuntu-slim ubuntu-18.04 ubuntu-20.04 windows-latest
@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown

Claude finished @tibrnui's task in 1m 32s —— View job


Code Review in Progress

Todo List:

  • Read .github/code_review.md for review standards
  • Read CLAUDE.md for project conventions
  • Review the full diff with git diff origin/main...HEAD
  • Read each changed file in full for context
  • Perform cross-file consistency checks
  • Check for missing changes or edge cases
  • Post comprehensive review feedback

Starting review now...

@uipreliga uipreliga left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: coder_eval — pr:86 (8 files) axis:1,4,5,6,7,8

Scope: pr:86 (8 files) axis:1,4,5,6,7,8 · branch chore/migrate-runners-to-uipath-images-7a21e4d2 · 027121e · 2026-08-06T11:26Z · workflow variant

Change class: simple — a mechanical, repo-wide CI infrastructure swap (8 workflow files: runner labels ubuntu-latest -> uipath-ubuntu-latest plus a few action SHA pins); no application code, but it changes the execution substrate for every gate in a PUBLIC repo, so the blast radius is broader than the line count suggests

Architecture and API surface are flawless (10/10) and the change itself is a mechanically clean runner migration, but it is unvalidated where it matters most — uipath-windows-latest/uipath-ubuntu-24.04 are first-ever labels carrying eval jobs whose wall-clock scoring criteria and unpinned pytest concurrency make agent scores runner-dependent, untrusted fork-PR code now executes on the org-shared pool with no fork guard on quality-gate/no-uipath-extra, and four release/publish/docs workflows get zero PR-time validation while three release.yml comments now assert the exact opposite of the code they annotate; bottom line: sound direction, merge only after adding fork guards, correcting the stale comments, and staging or canarying the eval-job and release-path labels.

Summary

Axis Score 🔴 🟠 🟡 🔵 Top Issue
1. Code Quality & Style 8.3 / 10 0 1 1 2 release.yml: release + publish-pypi jobs moved to uipath-ubuntu-latest while three in-file comments (52-54, 62-66, 411-414) still assert they are GitHub-hosted precisely to avoid that pool, and the hardcoded SAFE_CHAIN exclusion list the comment calls a no-op is now live
4. Security 9.4 / 10 0 0 1 1 Untrusted fork-PR code now executes on the org-shared uipath-* runner pool: quality-gate and no-uipath-extra have no fork guard (repo's own release.yml calls that pool "self-hosted")
5. Architecture & Design 10 / 10 0 0 0 0
6. Error Handling & Resilience 8.8 / 10 0 0 2 2 Release/publish/docs path (4 dispatch- or push-only workflows, 5 runs-on lines) gets no pull_request validation of the new pool — and release.yml:55 now contradicts its own "GitHub-hosted so cutting a release does not depend on the self-hosted uipath-ubuntu-latest pool" comment at :51-54
7. API Surface & Maintainability 10 / 10 0 0 0 0
8. Evaluation Harness Quality 9.5 / 10 0 0 1 0 windows-smoke and the three live-test jobs move to uipath-windows-latest / uipath-ubuntu-24.04 — labels never used before in this repo, and deliberately skipped by the previous runner migration — while keeping GitHub-hosted-tuned timeouts, shell: bash/jq image assumptions, and a 10s wall-clock scoring criterion

Overall Score: 9.3 / 10 · Weakest Axis: Code Quality & Style at 8.3 / 10
Totals: 🔴 0 · 🟠 1 · 🟡 5 · 🔵 5 across 6 axes.

Blockers

  1. [Axis 1] release.yml: release + publish-pypi jobs moved to uipath-ubuntu-latest while three in-file comments (52-54, 62-66, 411-414) still assert they are GitHub-hosted precisely to avoid that pool, and the hardcoded SAFE_CHAIN exclusion list the comment calls a no-op is now live (.github/workflows/release.yml:52) — Three comment blocks in release.yml now assert the opposite of the code they annotate, and two sit immediately above the runs-on: lines this PR rewrote.

(a) Lines 52-55 (verbatim at PR HEAD):

    # GitHub-hosted so cutting a release does not depend on the self-hosted
    # `uipath-ubuntu-latest` pool. semantic-release, uv, twine, and the docker
    # buildx -> GHCR push all run fine here.
    runs-on: uipath-ubuntu-latest

The comment states a deliberate resilience constraint ('cutting a release does not depend on the self-hosted uipath-ubuntu-latest pool') and the very next line now makes the release job depend on exactly that pool. A reader debugging a failed release is told the wrong thing about which pool ran it.

(b) Lines 62-66:

      # The self-hosted `uipath-ubuntu-latest` runners enforce a minimum
      # package-age safe-chain check on uv installs; on GitHub-hosted runners
      # this is a no-op. Kept (matching pr-checks.yml) so parity is preserved
      # if the job ever moves back to the self-hosted pool.
      SAFE_CHAIN_MINIMUM_PACKAGE_AGE_EXCLUSIONS: "openai-codex-cli-bin,openai-codex"

'if the job ever moves back to the self-hosted pool' is now past tense - it has moved. The comment also documents this env var as a no-op; after this PR it is load-bearing (the safe-chain min-age check is active on the release job's uv sync), so the hardcoded two-package exclusion list is now on the critical release path rather than dormant.

(c) Lines 411-419:

  # Publish the wheel+sdist to public PyPI. This runs as its own job so OIDC
  # Trusted Publishing is scoped to a dedicated, environment-gated context on
  # GitHub-hosted runners -- no PyPI token/secret is stored. Gated on the
  # release job having actually cut a version.
  publish-pypi:
    ...
    runs-on: uipath-ubuntu-latest

'on GitHub-hosted runners' is no longer true of the job it describes.

Also note the repo now contradicts itself about what the pool is: the PR title calls it a 'centralized managed GitHub pool', while release.yml's own prose (unchanged by this PR) calls uipath-ubuntu-latest 'self-hosted' three times (lines 52, 62, 65). I could not determine from the diff or from anything in the repo which is correct - nothing in .github/, CONTRIBUTING.md, or CLAUDE.md documents the pool. What would settle it: gh api repos/UiPath/coder_eval/actions/runners or the org runner-group settings showing whether these labels map to GitHub-hosted larger runners or to self-managed VMs.

Fix: rewrite all three comment blocks in this PR to state the new reality (and for (b), say plainly that the safe-chain exclusion list is now active on the release path). If the release job's insulation from the pool was intentional, say so on the PR and keep release/publish-pypi on ubuntu-latest instead of deleting the rationale silently.

Non-blocking, but please consider before merge

  1. [Axis 1] docs.yml pins actions/checkout to a v4.4.0 SHA while every other checkout call site in the repo pins v6.0.2 (.github/workflows/docs.yml:40) — The PR converts docs.yml's last floating tag to a SHA:
      - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262  # v4.4.0

I verified gh api repos/actions/checkout/git/ref/tags/v4.4.0 -> 11d5960a326750d5838078e36cf38b85af677262, so the pin is faithful to the old @v4 - but every other actions/checkout reference at PR HEAD (14 of them: claude-pr-review.yml:51, codeql.yml:28, conventional-commits.yml:41, docker-publish.yml:54, pr-checks.yml:44/170/237/368/532/699/771/842, publish-testpypi.yml:47, release.yml:82) is @de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2. The PR touched this exact line and left the repo with two pinned majors of the same action.

This is worse than the @v4 it replaced in one specific way: @v4 silently tracked the v4 line, whereas a SHA freezes it. I confirmed the runtime difference from each pinned tree's action.yml via the GitHub contents API: v4.4.0 is using: node20, v6.0.2 is using: node24. node20 is the runtime GitHub is retiring, and 'does the new pool's image still ship the node20 action runtime' is precisely the class of assumption this migration should not quietly take on.

Fix: pin docs.yml to the repo-standard actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 so all 15 references agree. (Dependabot's github-actions block groups patterns: ["*"] with no ignore list, so it will eventually raise this - but as a second, divergent bump rather than converging the repo.)
2. [Axis 4] Untrusted fork-PR code now executes on the org-shared uipath- runner pool: quality-gate and no-uipath-extra have no fork guard (repo's own release.yml calls that pool "self-hosted")* (.github/workflows/pr-checks.yml:33) — The repo is PUBLIC (gh api repos/UiPath/coder_eval -> "visibility":"public"). pr-checks.yml:3-5 triggers on on:\n pull_request:\n branches: [main, develop], and two jobs carry NO fork carve-out -- unlike their siblings windows-smoke (line 219), e2e-smoke (line 331-ish), live-tests, codex-live-tests, byoa-live-tests and action-dogfood, which all guard with if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository:

  • quality-gate, line 33: runs-on: uipath-ubuntu-latest
  • no-uipath-extra, line 166: runs-on: uipath-ubuntu-latest
    Both execute fully attacker-controlled content from the fork: line 74 run: uv sync --frozen --extra dev --extra uipath --extra codex resolves the PR's own uv.lock (build hooks = arbitrary code), and line 139 .venv/bin/pytest tests/ -v \ runs the PR's own test files. Same shape at codeql.yml:19 and conventional-commits.yml:19, both pull_request-triggered with no fork guard (those two do not execute repo code, so they are lower risk).

Before this PR that arbitrary code ran on a stock, vendor-neutral actions/runner-images VM. After it, it runs inside UiPath's custom image (VM Image / Source: Custom / Name: uipath-ubuntu-24.04 / Version: 1.7.0, per the PR's own job log) in org runner group 1 ("default"), alongside an admin pre-job hook /opt/scg/job-started.sh that PATH-shims uv/pip/python. Any GitHub user opening a fork PR gets a shell inside that image and can read whatever is baked into it (internal CA certs, package-manager configs, tokens, preinstalled internal tooling) and consume the shared org pool's capacity. The shims live in /home/runner/.safe-chain/shims on PATH, so the untrusted code they are meant to police can trivially drop them from PATH -- the control is advisory, not enforcing, on exactly the jobs that need it.

What I could and could not verify, so this is not filed higher: I DID establish the pool is ephemeral GitHub-hosted compute (Runner Image Provisioner / Hosted Compute Agent), which rules out the persistent-runner classes -- credential/tool-cache poisoning across jobs, runner-registration-token theft, and lateral movement from a corporate network position. I could NOT verify the custom image's contents or the runner group's "allow public repositories" scoping (gh api orgs/UiPath/actions/runner-groups -> HTTP 403). CONFIRM/REFUTE: publish the image build definition and confirm it bakes in no credentials, internal feed configs, or internal-network-only certs; and confirm the runner group's public-repository access setting. Note secrets are NOT a widening here -- GitHub withholds secrets from fork pull_request runs, so SAFE_CHAIN_MINIMUM_PACKAGE_AGE_EXCLUSIONS at line 39 resolves empty for forks.

Fix: gate untrusted-fork execution off the custom pool -- either add the same head.repo.full_name == github.repository guard used by the sibling jobs and add a fork-only stock-ubuntu-latest variant, or make the runner label an expression that falls back to ubuntu-latest for fork PRs. Combine with "Require approval for all external contributors" in the repo's Actions settings.

CVSS reasoning: AV:N -- the attack is delivered by opening a pull request over the network; PR:N -- any unauthenticated GitHub account can open a fork PR, no repo privileges needed; UI:N -- no maintainer action is required (there is no pull_request_target and no approval gate declared in-repo); AC:H -- success depends on the unreviewed image actually containing something worth stealing, a condition beyond the attacker's control; S:C -- code escapes the workflow's own security authority into the org's shared runner-group resources; C:L/A:L for image-baked material disclosure and shared-pool capacity consumption; I:N because the runner is ephemeral, so nothing persists to corrupt. CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:L/I:N/A:L (5.4)
3. [Axis 6] Release/publish/docs path (4 dispatch- or push-only workflows, 5 runs-on lines) gets no pull_request validation of the new pool — and release.yml:55 now contradicts its own "GitHub-hosted so cutting a release does not depend on the self-hosted uipath-ubuntu-latest pool" comment at :51-54 (.github/workflows/docker-publish.yml:44) — Triggers verified by reading each file: docker-publish.yml:21-24 on: / push: / branches: [main] / workflow_dispatch:; release.yml:25-26 on: / workflow_dispatch:; publish-testpypi.yml:24-25 on: / workflow_dispatch:; docs.yml:26-27 on: / workflow_dispatch:. None of these fire on pull_request, so the four runner-label changes at docker-publish.yml:44, release.yml:55, release.yml:419, publish-testpypi.yml:37 and docs.yml:38 get ZERO validation from this PR's checks. Those four workflows are precisely the ones with the least tolerance for a first-run surprise and the most pool-specific prerequisites: docker-publish.yml needs a working Docker daemon for docker/setup-buildx-action (:68) and a registry buildcache round-trip (:91-92); release.yml + publish-testpypi.yml need OIDC id-token minting plus outbound egress to pypi.org; docs.yml needs git push --force egress to github.com (:74). A missing prerequisite is therefore first discovered on a push to main (docker-publish) or mid-release (release.yml), i.e. at the worst possible time. Note also that docker-publish.yml's three action pins are re-pinned to SHAs in the same untested-by-PR file — I verified all four new pins resolve to the commented tags via gh api repos/<a>/git/ref/tags/<tag> (setup-buildx v3.12.0, login-action v3.7.0, build-push-action v6.19.2, checkout v4.4.0 all match), so the pins themselves are correct; the exposure is the runner change, not the SHAs. Fix: before merge, workflow_dispatch each of the four on the PR branch against the new pool (release.yml supports a non-main prerelease dispatch by design, publish-testpypi.yml is a dedicated dry run) and record the results in the PR; or stage the migration so these four move only after the pull_request-exercised jobs have run green on the pool for a period.
4. [Axis 6] Migrated package-installing jobs (incl. action-dogfood's uv tool install) now run under the pool's safe-chain package-age gate without setting the exclusions env that other install jobs set (.github/workflows/pr-checks.yml:166) — release.yml:62-64 states as fact that # The self-hosted \uipath-ubuntu-latest` runners enforce a minimum/# package-age safe-chain check on uv installs; on GitHub-hosted runners/# this is a no-op.Every job now on that pool that installs packages therefore runs under a gate that did not previously apply — but the exclusions env is applied inconsistently. Set on: pr-checks.yml:39, :225, :344, :522, :695, :767 (allSAFE_CHAIN_MINIMUM_PACKAGE_AGE_EXCLUSIONS: ${{ secrets.SAFE_CHAIN_MINIMUM_PACKAGE_AGE_EXCLUSIONS }}). NOT set on three migrated jobs that install packages: no-uipath-extra(pr-checks.yml:166runs-on: uipath-ubuntu-latest; installs at :180 pip install uvand :186uv pip install --python .venv/bin/python -e ".[dev]"); action-dogfood(pr-checks.yml:837; :852run: npm install -g @anthropic-ai/claude-codeplus the composite action'suv tool installat action.yml:100); andpublish-testpypi(publish-testpypi.yml:37;uv buildresolves build deps). Separately, release.yml:64-66 claims# Kept (matching pr-checks.yml) so parity is preserved/# if the job ever moves back to the self-hosted pool.aboveSAFE_CHAIN_MINIMUM_PACKAGE_AGE_EXCLUSIONS: "openai-codex-cli-bin,openai-codex"— that does NOT match pr-checks.yml, which sources the value from a secret; and this PR is exactly the 'ever moves back' event the comment anticipated, so the release job now runs under enforcement with a hardcoded two-entry list while PR CI runs with the (unknown, presumably broader) secret list. Result: a package the secret list tolerates can fail the release install but pass PR CI. Fix: set the same${{ secrets.SAFE_CHAIN_MINIMUM_PACKAGE_AGE_EXCLUSIONS }}on the three jobs missing it, replace release.yml:66's hardcoded literal with the secret (or vice-versa, consistently), and correct or delete the now-false parity comment at :64-65. Verifiability note: the secret's contents and the pool's actual safe-chain configuration are not readable from the repo — the inconsistency is certain, its blast radius is not; check the secret's value against the codex/claude-code install steps to size it. 5. **[Axis 8] windows-smoke and the three live-test jobs move touipath-windows-latest/uipath-ubuntu-24.04— labels never used before in this repo, and deliberately skipped by the previous runner migration — while keeping GitHub-hosted-tuned timeouts,shell: bash/jq image assumptions, and a 10s wall-clock scoring criterion** (.github/workflows/pr-checks.yml:211) — Line 211 becomes runs-on: uipath-windows-latestdirectly above a comment that is explicitly a GitHub-hosted-capacity tuning note (lines 212-217): # 15min headroom: the smoke task itself completes in ~7min, but the/ # actions/cache post-step on Windows is slow when ``.venv`` is large. timeout-minutes: 15. The same job relies on image contents it never installs — defaults: run: shell: bash(lines 221-223) andjq at line 313 (SUCCEEDED=$(jq '[.variant_aggregates[].tasks_succeeded] | add' "$F")), both guaranteed on GitHub's windows-latestimage, unverified onuipath-windows-latest. Capacity is not merely a job-timeout concern on this axis: the smoke task it runs at line 306 is tasks/hello_date.yaml, whose third criterion is - type: "run_command"/command: "python app.py"/timeout: 10— a 10-second wall-clock scoring gate. On a contended shared pool or a CPU-quota'd container, a coldpython app.pythat exceeds 10s scores that criterion 0 for identical agent output; the same task is scored bye2e-smokeandaction-dogfoodtoo. Separately,live-testsruns pytest with-n auto(line 600: -m live -v --tb=short --strict-markers -ra -n auto `), so worker count — and therefore concurrent real-API pressure and retry/timeout behaviour — is derived from the pool's vCPU count. Recommendation: migrate in stages (start with conventional-commits/codeql/quality-gate, keep the six eval jobs on GitHub-hosted labels until the pool's vCPU/RAM and image contents are published), and pin -n to a fixed worker count in the live-test steps so API concurrency stops being a function of the runner. CONFIRM/REFUTE: publish the pool's vCPU/RAM per runner and compare against ubuntu-latest (4 vCPU, as docs/tutorials/02-ci-pipeline.md already assumes); parity refutes the capacity half of this finding.

Nits

  1. [Axis 1] No .github/actionlint.yaml declares the new custom runner labels, so actionlint reports 16 runner-label errors on the migrated workflows (.github/workflows/pr-checks.yml:211) — ls -a .github/ confirms there is no actionlint.yaml. Running actionlint over the PR head yields 16 [runner-label] diagnostics, one per migrated runs-on: - e.g. at pr-checks.yml:211 (runs-on: uipath-windows-latest): label "uipath-windows-latest" is unknown ... if it is a custom label for self-hosted runner, set list of labels in actionlint.yaml config file.

I grepped the repo and actionlint is not wired into any gate today: .pre-commit-config.yaml has no actionlint hook, no workflow invokes it, make lint runs only the CE rule suite, and grep -rn actionlint returns only prose mentions in tests/test_custom_lint.py:1387, tests/lint/workflow_outputs.py:14/203, and .claude/harness-candidates.md:92, where 'actionlint + zizmor over .github/workflows/**' is listed as an explicitly DEFERRED harness candidate. So this is advisory rather than a broken gate - hence Low.

Fix (cheap, and it unblocks the deferred candidate):

# .github/actionlint.yaml
self-hosted-runner:
  labels:
    - uipath-ubuntu-latest
    - uipath-ubuntu-24.04
    - uipath-windows-latest

This PR is the natural home for it - it is the change that makes the labels custom, and the file doubles as the only in-repo declaration of which pool labels are legitimate.
2. [Axis 1] Two whitespace-only comment edits in pr-checks.yml are undeclared scope creep in an otherwise mechanical PR (.github/workflows/pr-checks.yml:170) — The diff includes two lines whose only change is a second space before the trailing pin comment:

-        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd  # v6.0.2
-        uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
+        uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065  # v5.6.0

(pr-checks.yml:170 and :173 at PR HEAD.)

To be fair to the change: I checked and these were the repo's only two single-space outliers against 52 two-space instances across the 8 workflows on main, so the edit is a genuine normalization, not damage. The nit is purely that it is undeclared scope creep in a PR whose stated purpose is a mechanical runs-on: swap - a reviewer scanning git diff --stat sees pr-checks.yml at +10/-10 and has to work out that 2 of those 10 are cosmetic. Either mention it in the PR description or split it into a separate style: commit; do not revert it.
3. [Axis 4] The repo's only SAST job (CodeQL) moved onto the same pool with no fallback label and no staged rollout, so a pool outage silently lapses security scanning (.github/workflows/codeql.yml:19) — codeql.yml:19 changes runs-on: ubuntu-latest to runs-on: uipath-ubuntu-latest. This is the repo's only SAST gate, and per codeql.yml:3-10 it runs on push, pull_request [main, develop], merge_group and schedule: - cron: '0 6 * * 1' # Weekly on Mondays at 6am UTC. After this PR ZERO jobs anywhere in the repo target a GitHub-hosted label, so there is no fallback and no canary: a pool capacity exhaustion or misconfiguration leaves the job queued until GitHub cancels it. On the PR/merge paths that is fail-closed (branch protection holds the merge), but the weekly scheduled scan -- the run that catches advisories in code already on main -- fails silently, with no continue-on-error, no alerting, and nobody watching a schedule-only signal. This is also the very job that produced the 27 open alerts routed to this review, so degrading it degrades the review signal itself. Combined with Finding #2, an unauthenticated actor opening fork PRs can consume the same shared pool the scheduled scan depends on.

Fix: keep at least the scheduled codeql.yml run (and ideally one canary job) on stock ubuntu-latest until the pool has an operational track record, or add failure alerting on the scheduled workflow so a lapsed scan is noticed. A repo-level .github/actionlint.yaml declaring self-hosted-runner: labels: [uipath-ubuntu-latest, uipath-ubuntu-24.04, uipath-windows-latest] would also silence the 16 informational runner-label diagnostics so a genuinely typo'd label (which would queue forever, exactly like an outage) is caught in lint instead of at runtime.

CVSS reasoning: AV:N (fork PRs / scheduled network-triggered runs); PR:N and UI:N because opening PRs against a public repo needs no privilege or maintainer action; AC:H because it requires pool contention or an outage to coincide with a scan window; S:U -- the loss is confined to this repo's CI; C:N/I:N/A:L -- the only impact is reduced availability of the security-scanning signal. CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L (3.7)
4. [Axis 6] docs.yml's migrated job has no timeout-minutes at all — on a shared pool a hung step holds a runner slot for GitHub's 6h default (.github/workflows/docs.yml:38) — git show pr-86:.github/workflows/docs.yml | grep -c timeout-minutes returns 0 — the publish job whose line 38 this PR changes to runs-on: uipath-ubuntu-latest declares no job timeout, so it inherits GitHub's 6-hour default. The job's last step is an unauthenticated-retry-free network push, docs.yml:74-75: git push --force \ / "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" gh-pages. On an ephemeral GitHub-hosted VM a hang costs only that VM; on a shared pool it occupies a slot other jobs (including the five required checks) are queueing for, turning one stuck docs publish into repo-wide CI latency. Every other migrated job in this PR declares a budget (pr-checks.yml:34/167/217/330/513/684/760/838, codeql.yml:20, conventional-commits.yml:20, claude-pr-review.yml:26, docker-publish.yml:48, publish-testpypi.yml:38, release.yml:56/420) — docs.yml is the only omission. Add timeout-minutes: 10 under line 38.
5. [Axis 6] e2e-smoke's failure path opens an interactive tmate reverse-SSH session that now blocks a shared, possibly non-ephemeral pool runner (.github/workflows/pr-checks.yml:492) — pr-checks.yml:492-496 reads - name: Debug SSH session on failure / if: failure() / uses: mxschmitt/action-tmate@35b54afac29c97fb54faba5b513f8fbd1882f113 # v3.x / with: / limit-access-to-actor: true. action-tmate blocks until an operator disconnects or the job timeout fires, so with timeout-minutes: 10 at :330 every e2e-smoke failure now pins a pool runner for the remainder of the budget rather than an ephemeral GitHub-hosted VM, and establishes an outbound SSH tunnel from inside the org pool to tmate.io on an unattended, automatically-triggered path (if: failure(), not a manual opt-in). E2E Smoke Tests (Real API) is a required check, so this fires on ordinary PR failures, not only on investigations. Fix: gate the step on an explicit opt-in that is inert by default — e.g. if: failure() && github.event_name == 'workflow_dispatch' && inputs.debug-tmate — so the shared runner is held (and the tunnel opened) only when someone asked for it. Scope note: the step itself is unchanged by this PR; the runner-label change at :329 is what changes its cost and trust boundary.

What's Missing

Downstream consumers:

  • 🟠 action-dogfood (pr-checks.yml:837) is the executable reference the published Action's onboarding contract is pinned to — tests/lint/action_docs.py::DOGFOOD_JOB parses this job and CE026 fails the build if the documented prerequisites drift from it — yet it now runs ONLY on uipath-ubuntu-latest, while every consumer-facing snippet it validates targets a GitHub-hosted runner (docs/tutorials/02-ci-pipeline.md:78 runs-on: ubuntu-latest; docs/CI_GATE.md:31,77 uses: UiPath/coder_eval@v0). After this PR nothing in CI proves the Marketplace composite (action.yml: shell: bash, python3, uv tool install) still works on the image external integrators actually use. Keep action-dogfood on ubuntu-latest, or run it as a two-leg matrix (ubuntu-latest + uipath-ubuntu-latest) so the consumer-facing leg stays proven. (trigger: .github/workflows/pr-checks.yml)
  • 🟡 Every capacity-derived knob in the repo was inherited unchanged from GitHub-hosted tuning and none was re-justified against the new spec: timeout-minutes values whose comments explicitly cite GitHub-hosted behavior (pr-checks.yml:212-217 windows-smoke 15min; quality-gate 10min), pytest -n auto (global addopts in pyproject.toml, live at pr-checks.yml:588/600, with :725 already needing an -n0 override), and tasks/hello_date.yaml's third criterion run_command … timeout: 10 — a wall-clock gate that scores 0 (not "infra error") if a contended runner makes python app.py slow, on the task run by windows-smoke, e2e-smoke, and action-dogfood alike. The repo's only written vCPU anchor remains docs/tutorials/02-ci-pipeline.md:61 ("ubuntu-latest = 4"), which now describes nothing this repo runs on. (trigger: .github/workflows/pr-checks.yml) (restates: Axis 8: GitHub-hosted-tuned timeouts and a 10s wall-clock scoring criterion carried onto an unspecified pool)

Parallel paths:

  • 🟠 The migration is total only against today's main, and nothing was added to hold the line: the in-flight branch fix/verify-published-action adds a 9th workflow (.github/workflows/verify-published-action.yml, jobs at :45 preflight and :292, plus a nightly schedule: 17 6 * * *) with runs-on: ubuntu-latest on both jobs. It will merge cleanly (no textual conflict with this PR) and silently reintroduce GitHub-hosted labels. Nothing in this PR — no .github/actionlint.yaml, no lint check, no PR-template/CODEOWNERS note (.github/CODEOWNERS covers /.github/ only by owner, not by rule) — makes that visible at review time. (trigger: .github/workflows/pr-checks.yml)
  • 🟡 The safe-chain exclusions env is applied to only 6 of the jobs that now run under the pool's package-age gate (pr-checks.yml:39/225/344/522/695/767, all ${{ secrets.SAFE_CHAIN_MINIMUM_PACKAGE_AGE_EXCLUSIONS }}) while three migrated package-installing jobs set nothing — no-uipath-extra (pr-checks.yml:166; installs at :180/:186), action-dogfood (pr-checks.yml:837; npm install -g @anthropic-ai/claude-code at :852 plus uv tool install in action.yml:100), and publish-testpypi (publish-testpypi.yml:37; uv build) — and release.yml:66 / docker-publish.yml:49 hardcode a two-entry literal instead of the secret. Same pool, same dependency set, three different enforcement postures. (trigger: .github/workflows/pr-checks.yml) (restates: Axis 6: Migrated install jobs run under the safe-chain gate without setting the exclusions env)
  • 🟡 No prose surface was updated to match a 100% migration: CONTRIBUTING.md (which documents the CI gates at :93 and owns the § Releasing runbook that CLAUDE.md points at) and CLAUDE.md's action.yml / release-pipeline note say nothing about which pool CI runs on, what its labels are, or how a contributor gets a fork PR to run. The only prose about the pool in the entire repo is release.yml:52-54 / :62-65, which this PR makes false — so after merge the repo's sole documentation of uipath-ubuntu-latest describes the opposite of the code. (trigger: .github/workflows/release.yml) (restates: Axis 1: release.yml comments still assert the jobs are GitHub-hosted to avoid that pool)

Tests:

  • 🟠 No canary or staged validation accompanies a 16-job, zero-fallback migration: 5 of the 16 changed runs-on lines (release.yml:55, release.yml:419, publish-testpypi.yml:37, docs.yml:38, docker-publish.yml:44) sit in workflows that fire only on workflow_dispatch / push:[main], so the PR merges with no evidence at all for the pool's OIDC id-token minting, buildx + GHCR push + registry buildcache, or the gh-pages force-push. The PR should carry a recorded workflow_dispatch run of each (publish-testpypi.yml exists precisely as the dry run; release.yml supports a non-main prerelease dispatch) or stage the move so these four land after the PR-exercised jobs have a green track record. (trigger: .github/workflows/release.yml) (restates: Axis 6: Release/publish/docs path gets no pull_request validation of the new pool)
  • 🟡 Nothing mechanically constrains the new label vocabulary: no .github/actionlint.yaml with self-hosted-runner.labels, no actionlint/zizmor hook in .pre-commit-config.yaml or Makefile (grep: zero hits), and no lint assertion — even though tests/lint/action_docs.py already YAML-parses .github/workflows/pr-checks.yml for the dogfood job, so a runs-on allowlist check is a handful of lines from existing infrastructure. Consequence: a typo'd label (uipath-ubunut-latest) is indistinguishable from a pool outage — the job queues until GitHub cancels it, hours later, instead of failing in make lint. (trigger: .github/workflows/pr-checks.yml) (restates: Axis 1: No .github/actionlint.yaml declares the new custom runner labels)
  • 🟡 uipath-windows-latest appears for the first time in repo history here (the previous migration, commit b473069, deliberately scoped itself to ubuntu-latest and left windows-smoke alone), yet no cheap pre-flight step was added for the two image contents windows-smoke consumes but never installs: defaults.run.shell: bash (pr-checks.yml:220-222, needs Git Bash) and jq (pr-checks.yml:313). A three-line bash --version && jq --version step at the top of the job turns an unknown-image failure into an immediate, unambiguous diagnostic instead of a mid-job parse error 7 minutes in. (trigger: .github/workflows/pr-checks.yml) (restates: Axis 8: windows-smoke and live-tests move to never-before-used labels)

Daily/nightly:

  • 🟠 The PR states nothing about downstream nightly impact, and there is real blast radius: docker-publish.yml (push-to-main, now uipath-ubuntu-latest) publishes ghcr.io/<owner>/coder-eval-agent:latest, which release.yml:360 documents verbatim as the tag "the nightly treats as tip-of-main", and release.yml:22 documents the ADO nightly pinning coderEvalVersion to what release.yml/publish-testpypi.yml publish. Both publish paths moved to the pool in the same commit, both are unvalidated by any PR-triggered job, and neither has failure alerting — a buildx/GHCR/egress gap freezes the nightly's image and version pin at the last good build with no red check anywhere. (trigger: .github/workflows/docker-publish.yml)
  • 🔵 The weekly scheduled CodeQL scan (codeql.yml:19, cron: 0 6 * * 1) is the one migrated path whose failure mode is silent — on PR/merge_group the branch protection holds the merge, but a schedule-only lapse has no continue-on-error, no alerting, and no watcher. The PR neither keeps the scheduled leg on a GitHub-hosted label nor adds failure notification, so "security scanning quietly stopped running" is an unstated outcome of the migration. (trigger: .github/workflows/codeql.yml) (restates: Axis 4: CodeQL, the repo's only SAST job, moved with no fallback label or staged rollout)

Display & mapping dicts:

  • 🟡 The label→runner mapping has no declared home: 16 literal label strings are duplicated across 8 workflow files with no indirection (runs-on: ${{ vars.CE_RUNNER_UBUNTU || 'ubuntu-latest' }}), no .github/actionlint.yaml labels list, and no matrix/env seam. So the rollback for a pool outage — and the per-job fork fallback the security finding asks for — is a 16-line hand edit under time pressure rather than flipping one repository variable. Route the labels through repo-level vars (or at minimum add the actionlint labels file as the one in-repo declaration of which labels are legitimate). (trigger: .github/workflows/pr-checks.yml)

Harness & Lint Improvements

Static checks (lint / type):

  • [ce-lint] CE036 — runner-label registry parity. New module tests/lint/workflow_runners.py, wired as tests/test_custom_lint.py::TestCE036RunnerLabelRegistry (a doc/whole-tree rule like CE026–CE031/CE035, NOT a BaseRuletests/lint/runner.py is AST-only over *.py). Rule: every literal runs-on: value across .github/workflows/*.yml (skip ${{ }} expressions and matrix.* indirection) must appear in exactly one declared registry — either a small constant GitHub-hosted allowlist in the rule module (ubuntu-latest, ubuntu-24.04, windows-latest, …) or the self-hosted-runner: labels: list of a new .github/actionlint.yaml. Ship the rule together with that file (uipath-ubuntu-latest, uipath-ubuntu-24.04, uipath-windows-latest), which becomes the single in-repo declaration of which pool labels are legitimate and of each label's provenance class. State of the tree today: 16 runs-on: sites, all custom labels, zero declarations. Cost: ~40 lines (yaml.safe_load + set membership). Prevents: Directly the 🔵 "no .github/actionlint.yaml, 16 actionlint [runner-label] errors" finding (anchored at pr-checks.yml:211 / claude-pr-review.yml:25). Also closes the failure mode called out in the CodeQL-availability finding (codeql.yml:19): a typo'd label (uipath-ubunut-latest) is not a runtime error — the job queues until GitHub cancels it, and on the schedule-only weekly scan nobody notices. With CE036 it fails in make lint. It is also the prerequisite registry that CE037, CE040 and CE041 below read.
  • [ce-lint] CE037 — runner-provenance comment parity. Same module/test class family as CE036 (tests/lint/workflow_runners.py, TestCE037RunnerProvenanceComments). Operates on the raw text (comments are dropped by yaml.safe_load, so scan lines, mapping each comment to the enclosing job block by indentation). For every job: if a comment inside the job block (or in the 15 lines immediately above its runs-on:) contains the phrase GitHub-hosted or self-hosted, that claim must agree with the provenance class CE036's registry assigns to the job's actual runs-on: label; second arm — if such a comment names a runner label from the registry other than the job's own, that is also a finding. Escape hatch: an explicit # provenance-note: <reason> line for legitimately historical prose. Cost: ~50 lines, regex + indentation scoping. Prevents: The highest-severity finding in this review (5-axis cross-axis convergence, release.yml:52): all three now-false comment blocks fire — release.yml:52-54 ("GitHub-hosted so cutting a release does not depend on the self-hosted uipath-ubuntu-latest pool") above runs-on: uipath-ubuntu-latest; release.yml:62-65 ("on GitHub-hosted runners this is a no-op … if the job ever moves back to the self-hosted pool"); release.yml:411-414 ("…on GitHub-hosted runners") above the migrated publish-pypi job at :419. This is the exact class the reviewers flagged as unpreventable-by-eye: a 2-line mechanical runs-on: swap silently inverted three prose invariants, and no gate looked at prose.
  • [ce-lint] CE038 — action-pin uniformity + immutability. New tests/lint/workflow_pins.py, wired as TestCE038ActionPinUniformity. Three assertions over every uses: in .github/workflows/*.yml (skip local ./): (1) the ref must be a 40-hex commit SHA, never a floating @v3/@main; (2) it must be followed by exactly two spaces and a # v<tag> comment; (3) all references to the same owner/repo must share one SHA and one tag comment (allow a documented # pin-exception: <reason> for a deliberate split). Cost: ~35 lines of regex + a defaultdict(set). Prevents: The 🟡 "docs.yml pins actions/checkout to v4.4.0 while 14 other call sites pin v6.0.2" finding (docs.yml:40) — arm (3) fails it, and would also have flagged two divergences this review did not file: actions/upload-artifact at release.yml:241 (ea165f8d… v4.6.2 vs 043fb46d… at 8 sites) and actions/setup-python at pr-checks.yml:173 (a26af69b… v5.6.0 vs a309ff8b… at 8 sites). Arm (2) mechanizes the 🔵 "whitespace-only comment edits are undeclared scope creep" finding (pr-checks.yml:170/173) — the normalization becomes a gate instead of an ad-hoc hand edit inside an unrelated PR. Arm (1) subsumes and supersedes the deferred CE026 — forbid floating uses: refs candidate in .claude/harness-candidates.md (that id has since been taken by CE026 Action doc surfaces; renumber the candidate entry to CE038 when promoting).
  • [ce-lint] CE039 — every workflow job must declare timeout-minutes. Add as a second test in TestCE036RunnerLabelRegistry's module or its own TestCE039JobTimeoutRequired: yaml.safe_load each .github/workflows/*.yml, assert every entry under jobs: sets timeout-minutes (skip uses:-style reusable-workflow jobs, which cannot). Cost: ~15 lines. Prevents: The 🔵 "docs.yml's migrated publish job has no timeout-minutes at all" finding (docs.yml:38). Verified: docs.yml is the only workflow with zero timeout-minutes occurrences; every other migrated job declares one (pr-checks.yml ×8, codeql.yml, conventional-commits.yml, claude-pr-review.yml, docker-publish.yml, publish-testpypi.yml, release.yml ×2). On a shared pool the 6-hour GitHub default holds a runner slot that required checks are queueing for, so this went from cosmetic to load-bearing with the migration.
  • [ce-lint] CE041 — safe-chain exclusions parity on package-installing pool jobs. New test class TestCE041SafeChainEnvParity (reuses CE036's registry). For every job whose runs-on: is a registry-declared custom pool label and whose steps contain a package-installer invocation (pip install, uv sync, uv pip install, uv tool install, uv build, npm install, npm ci), assert the job (or workflow) env: sets SAFE_CHAIN_MINIMUM_PACKAGE_AGE_EXCLUSIONS, and that its value is the secret reference ${{ secrets.SAFE_CHAIN_MINIMUM_PACKAGE_AGE_EXCLUSIONS }} rather than a hardcoded literal. Cost: ~40 lines; the installer patterns are a small literal list. Prevents: The 🟠 "migrated install jobs run under the pool's package-age gate without setting the exclusions env" finding. Verified misses it would fire on: no-uipath-extra (pr-checks.yml:166; installs at :180/:186 with no env: block at all, while the sibling quality-gate does the identical .[dev] install with the secret at :39), action-dogfood (pr-checks.yml:837; npm install -g @anthropic-ai/claude-code at :852 plus the composite's uv tool install at action.yml:100), and publish-testpypi (publish-testpypi.yml:37). The literal arm fires on the two hardcoded "openai-codex-cli-bin,openai-codex" values — release.yml:66 and docker-publish.yml:50 — which is exactly the drift the (now false) "Kept (matching pr-checks.yml)" comment claimed was not happening: pr-checks sources six sites from a secret.
  • [bandit-codeql] CE040 — fork-exposure guard on custom-pool jobs (TestCE040ForkGuardOnPoolJobs, reusing CE036's registry): in any workflow whose on: includes pull_request or pull_request_target, every job whose runs-on: is a registry-declared custom-pool label must carry the repo's standard guard in if:github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository — or an explicit # fork-exposure-ok: <reason> line. Pair it with promoting the deferred .claude/harness-candidates.md entry "actionlint + zizmor over .github/workflows/**" into a real (initially non-blocking, then required) job: actionlint runs shellcheck over run: bodies and consumes the .github/actionlint.yaml CE036 introduces; zizmor's self-hosted-runner, excessive-permissions and template-injection rules are the off-the-shelf coverage for this exact class. No static analysis of any kind runs over workflow YAML today — make verify never looks at it. Prevents: The 🟠 security finding "untrusted fork-PR code now executes on the org-shared uipath-* pool" (pr-checks.yml:33 quality-gate and :166 no-uipath-extra, both unguarded, both executing the PR's own uv.lock build hooks at :74 and the PR's own test files at :139; same shape at codeql.yml:19 and conventional-commits.yml:19). Six sibling jobs already carry the exact guard string (pr-checks.yml:219/332/515/686/762/840), so the rule codifies an expression the repo already uses — note those six are secret-availability guards by their own comments, so CE040 is what turns an incidental convention into an enforced isolation boundary. The zizmor half also covers the 🔵 CodeQL-availability finding's request for a lint-time catch of bad labels.
  • [ce-lint] CE042 (lowest priority, threshold is a judgment call) — wall-clock scoring floors in tasks/*.yaml. Assert no run_command success criterion used by a CI job declares a timeout: below ~30s, or requires an inline justification. tasks/hello_date.yaml's third criterion is command: "python app.py" with timeout: 10 — a 10-second wall-clock gate that scores 0 (not "infra error") for identical agent output on a contended runner, and that task is scored by windows-smoke (pr-checks.yml:306), e2e-smoke and action-dogfood. Prevents: The capacity half of the 🟠 "windows-smoke and live-tests move to never-before-used labels while keeping GitHub-hosted-tuned assumptions" finding (pr-checks.yml:211). Verifiers judged this the weakest leg of that finding (a cold Python start would have to be ~100x slower to trip it), so file it as a cheap guard, not a blocker — the alternative framing is the runner-shape harness bullet below.

Harness improvements (not statically reachable):

  • Pool canary + A/B parity smoke. Add a scheduled (daily) workflow that runs one identical cheap smoke task — e.g. tasks/hello_date.yaml via the existing e2e path — twice: once on stock ubuntu-latest, once on uipath-ubuntu-latest, and fail if the outcomes or wall-clock differ beyond a band. Keep at least that one job on a GitHub-hosted label so the repo retains a warm fallback and a canary (after this PR, ZERO jobs anywhere target a GitHub-hosted label). Why not static: Requires actually scheduling work onto both runner pools and comparing observed outcomes/timings; the defect is a property of the machines and their baked images, not of any file in the repo. Prevents: The 🔵 "repo's only SAST job moved to the pool with no fallback and no canary" finding (codeql.yml:19), the capacity half of the pr-checks.yml:211 finding, and the general 'first-run surprise' exposure behind the docker-publish.yml:44 finding.
  • Pre-merge rehearsal for the four dispatch-only workflows. release.yml, publish-testpypi.yml, docs.yml and docker-publish.yml never fire on pull_request, so five migrated runs-on: lines (docker-publish.yml:44, release.yml:55, release.yml:419, publish-testpypi.yml:37, docs.yml:38) got zero validation from this PR's checks. Add a pool-rehearsal workflow_dispatch job that exercises precisely the three prerequisites no pull_request job covers on the new pool — OIDC id-token: write minting, docker/setup-buildx-action + GHCR login/push + registry buildcache round-trip, and the gh-pages git push --force — and add a line to CONTRIBUTING § Releasing requiring its run link in the PR before a runner-infrastructure change merges. Why not static: Needs real OIDC token minting, registry credentials and outbound egress from the pool; no file inspection can tell you whether the image can mint an id-token or reach ghcr.io. (Verifiers confirmed Docker/BuildKit, pypi.org and github.com egress ARE already exercised on the pool by pull_request jobs — these three genuinely are not.) Prevents: The 🟠 "release/publish/docs path gets no pull_request validation of the new pool" finding (docker-publish.yml:44 and the four sibling lines).
  • Failure alerting for schedule-only workflows. codeql.yml's weekly cron: '0 6 * * 1' scan (and the nightly verify-published-action.yml) have no notifier: a queue-forever or outage failure on a schedule-only trigger is observed by nobody, unlike PR-path failures which branch protection surfaces. Add a small if: failure() job that opens/updates a tracking issue (or posts to the team channel) on scheduled runs. Why not static: The defect is the absence of a runtime observer for a run nobody is looking at; lint can see the cron exists but not that its failures go unnoticed. Prevents: The 🔵 CodeQL finding (codeql.yml:19) — its core claim is that on the scheduled path the migration fails silently, degrading the very SAST signal this review consumes.
  • Image-contract preflight step on the new-label jobs. windows-smoke sets defaults: run: shell: bash (pr-checks.yml:220-222) and calls jq unguarded (pr-checks.yml:313) — both guaranteed on GitHub's windows-latest image, unverified on uipath-windows-latest, which (with uipath-ubuntu-24.04) appears for the first time in repo history in this PR's commit 027121e and was deliberately skipped by the earlier runner migration (b473069). Add a ~5-line first step per job asserting the tools it assumes but never installs (bash, jq, git, python, docker) with a clear failure message. Why not static: Image contents are only knowable by executing on the runner; the repo has no manifest of what the pool's custom image bakes in (and no doc anywhere in .github/, CONTRIBUTING.md or CLAUDE.md describes the pool at all). Prevents: The image-assumption half of the 🟠 pr-checks.yml:211 finding — turns a cryptic mid-job failure into a one-line preflight diagnosis.
  • Make evaluation scoring independent of runner shape, and record the runner in the run record. Pin -n <fixed> for the -m live pytest steps (pr-checks.yml:588/600) instead of inheriting pyproject.toml's repo-wide addopts = ["-n", "auto"], so real-API concurrency and retry/timeout pressure stop being a function of the pool's vCPU count; and stamp the runner label + vCPU/RAM into the run record so a suite-score delta is attributable to the machine rather than the agent. Why not static: Deciding whether a score moved because of the runner requires cross-run data from real executions; a lint can see -n auto but not that the resulting worker count changed the outcome. Prevents: The concurrency/scoring-determinism half of the 🟠 pr-checks.yml:211 finding (and gives the CE042 wall-clock proposal above an evidence base instead of a guessed threshold).
  • Document the runner pool's provenance and make that doc the SSOT the lint reads. The repo currently contradicts itself: the PR title says "centralized managed GitHub pool" while release.yml calls uipath-ubuntu-latest "self-hosted" three times (lines 52, 62, 65), and gh api orgs/UiPath/actions/runner-groups returned 403 to the reviewers, so nobody could settle it from the repo. Add a short CONTRIBUTING (or .github/README) section stating, per label: hosted vs self-managed, vCPU/RAM, whether the safe-chain package-age gate is enforced, what is baked into the image, and the runner group's public-repository scoping — and wire .github/actionlint.yaml's label list to it so CE036/CE037/CE040/CE041 all read one declaration. Why not static: The fact itself does not exist anywhere in the repo — a static check cannot verify a claim about org infrastructure until a human records it. Once recorded, four of the proposed CE rules become enforceable against it. Prevents: The root ambiguity underneath the top finding (release.yml:52) and the two security findings (pr-checks.yml:33, codeql.yml:19): all three had to be filed with an explicit CONFIRM/REFUTE caveat because the pool's nature is undocumented.

Top 5 Priority Actions

  1. SCORE-AFFECTING: stage or canary the four eval jobs onto the never-before-used uipath-windows-latest/uipath-ubuntu-24.04 labels (.github/workflows/pr-checks.yml:211) — tasks/hello_date.yaml:27's run_command criterion has a 10s wall-clock timeout: that scores 0 on a contended or CPU-quota'd runner for byte-identical agent output, and the prior migration (commit b473069) deliberately skipped exactly these two jobs.
  2. SCORE-AFFECTING: pin pytest worker count instead of inheriting -n auto from pyproject.toml addopts for the live-test jobs (.github/workflows/pr-checks.yml:588,600), so real-API concurrency, retry pressure and turn-timeout behaviour stop being a function of the new pool's vCPU count rather than the code under test.
  3. Add the fork carve-out used by the sibling jobs (github.event.pull_request.head.repo.full_name == github.repository) or a stock-ubuntu-latest fallback to quality-gate (.github/workflows/pr-checks.yml:33) and no-uipath-extra (:166), which today run attacker-controlled uv.lock build hooks (:74) and fork-authored pytest files (:139) on the shared org pool from any unauthenticated GitHub account.
  4. Rewrite the three now-false release.yml comment blocks in this PR — :52-54 ("GitHub-hosted so cutting a release does not depend on the self-hosted uipath-ubuntu-latest pool") sits directly above the runs-on: uipath-ubuntu-latest it contradicts, and :62-65 calls the safe-chain gate a no-op and "matching pr-checks.yml" when :66 hardcodes "openai-codex-cli-bin,openai-codex" while pr-checks sources the same var from a secret at six sites.
  5. Close the validation and consistency gaps on the untested release path: workflow_dispatch release.yml/publish-testpypi.yml/docker-publish.yml/docs.yml against the new pool before merge (OIDC id-token minting, buildx/GHCR push + registry cache, and the gh-pages force-push are genuinely unexercised), set SAFE_CHAIN_MINIMUM_PACKAGE_AGE_EXCLUSIONS on the three migrated install jobs missing it (pr-checks.yml:166, :837, publish-testpypi.yml:37), add timeout-minutes to the only job without one (.github/workflows/docs.yml:38), converge the lone stale checkout pin at docs.yml:40 (v4.4.0/node20) onto the repo-standard v6.0.2, and add .github/actionlint.yaml declaring the three custom labels to clear the 16 runner-label diagnostics.

Stats: 0 🔴 · 1 🟠 · 5 🟡 · 5 🔵 across 6 axes reviewed.

@uipreliga uipreliga left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We had problems in the past in that there were not enough workers for ubuntu- version on the public repos.

This resulted in all the jobs being stalled.

Is this fixed?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants