v1.92.39.0 ci: main pushes reuse the merged PR's verified run of the same tree; dispatch-ci and --from-run go through the run verifier (GSTA-156 P3b) (#3709)

* feat(pregate): gstack run profiles, dispatch-ci clears through the run verifier, --from-run tests stage (GSTA-156 P3b)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* ci(free-tests): main pushes reuse the merged pull request's verified run of the same tree (GSTA-156 P3b)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* test(cso): the free-tests aggregate still requires cso-docker-integration on its strict path, and only a verified reuse skips it (GSTA-156 P3b)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* fix(pregate): name the reuse profiles after no lane, so lane clears and ready --dispatch-ci keep the URL path until GST-555 (GSTA-156 P3b)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* fix(ci): the duration recorder grants actions: read to its windows-free-tests call, and --from-run refuses a run that recorded zero tests (GSTA-156 P3b)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* docs: pregate run_profiles row, no_verified_run code; changelog for caller grant and zero-test refusal

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

---------

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
Garry Tanandcapy-ai[bot] authored and GitHub committed 2026-11-01 20:30:10 +00:00
1 parent ab2852fb40
commit 77fe28c397
24 files changed
+1114 -66

No files matched your search

@@ -71,6 +71,9 @@ jobs:
windows:
uses: ./.github/workflows/windows-free-tests.yml
permissions:
contents: read
actions: read
with:
record_durations: true
+103 -2
View File
@@ -14,6 +14,15 @@ name: Free Tests
# is permanent false comfort.
#
# Local `bun run test` still uses the existing bounded process pool.
#
# One full suite per tree (GSTA-156, docs/pregate.md#ci-reuse). The aggregate
# uploads a `tested-tree` record (tree.json) only after strict verification
# passes. A push to main first runs `reuse`: when the merged pull request's
# head has the pushed tree and a green pull_request or workflow_dispatch run of
# this workflow verifies (lib/run-evidence.ts), every suite job is skipped and
# the aggregate links that run instead. Any other answer, a reuse failure
# included, runs every job as before. Pull request and dispatch runs never
# consume; reused runs never upload a record, so they are never reused.
on:
pull_request:
@@ -54,7 +63,40 @@ permissions:
contents: read
jobs:
# Main pushes only, and the one job with actions: read (it reads this
# repository's runs and their tested-tree artifacts). Every suite job below
# runs unless this job succeeded and reused a run; a skipped or failed reuse
# job runs them all. Their conditions start with !cancelled() because a
# skipped needed job would otherwise skip its dependents on pull_request and
# dispatch runs, where reuse never runs.
reuse:
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-24.04
timeout-minutes: 5
permissions:
contents: read
actions: read
outputs:
reused: ${{ steps.find.outputs.reused }}
run_id: ${{ steps.find.outputs.run_id }}
run_url: ${{ steps.find.outputs.run_url }}
tree: ${{ steps.find.outputs.tree }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
with:
bun-version: 1.4.2
- id: find
name: Find a green pull request or dispatch run of this exact tree
env:
GH_TOKEN: ${{ github.token }}
run: bun run scripts/ci-tree-reuse.ts find --workflow free-tests.yml --profile ci-reuse/free-tests
free-plan:
needs: reuse
if: ${{ !cancelled() && (needs.reuse.result != 'success' || needs.reuse.outputs.reused != 'true') }}
runs-on: ubuntu-24.04
timeout-minutes: 5
outputs:
@@ -106,6 +148,8 @@ jobs:
if-no-files-found: error
cso-macos-launcher:
needs: reuse
if: ${{ !cancelled() && (needs.reuse.result != 'success' || needs.reuse.outputs.reused != 'true') }}
runs-on: macos-latest
timeout-minutes: 15
steps:
@@ -144,6 +188,8 @@ jobs:
# default, and the job fails if that layout is not symlinked. It also sources
# the Codex probe from zsh, macOS's default shell (#3024; no paid call).
macos-named-regressions:
needs: reuse
if: ${{ !cancelled() && (needs.reuse.result != 'success' || needs.reuse.outputs.reused != 'true') }}
runs-on: macos-latest
timeout-minutes: 20
steps:
@@ -188,6 +234,8 @@ jobs:
bun test "${files[@]}"
cso-windows-launcher:
needs: reuse
if: ${{ !cancelled() && (needs.reuse.result != 'success' || needs.reuse.outputs.reused != 'true') }}
runs-on: windows-latest
timeout-minutes: 15
steps:
@@ -216,6 +264,8 @@ jobs:
retention-days: 14
cso-docker-integration:
needs: reuse
if: ${{ !cancelled() && (needs.reuse.result != 'success' || needs.reuse.outputs.reused != 'true') }}
runs-on: ubuntu-24.04
timeout-minutes: 20
steps:
@@ -246,6 +296,8 @@ jobs:
DOCKER_HOST: unix:///var/run/docker.sock
typecheck:
needs: reuse
if: ${{ !cancelled() && (needs.reuse.result != 'success' || needs.reuse.outputs.reused != 'true') }}
runs-on: ubuntu-24.04
timeout-minutes: 10
steps:
@@ -269,6 +321,8 @@ jobs:
# the quick tier also runs on it. Its pin equals GSTACK_BUN_DEFAULT;
# test/bun-version-drift.test.ts checks this job exists and agrees.
quick-bun-default:
needs: reuse
if: ${{ !cancelled() && (needs.reuse.result != 'success' || needs.reuse.outputs.reused != 'true') }}
runs-on: ubicloud-standard-8
timeout-minutes: 30
steps:
@@ -365,7 +419,8 @@ jobs:
run: xvfb-run -a bun run test:quick
free-suite:
needs: free-plan
needs: [free-plan, reuse]
if: ${{ !cancelled() && needs.free-plan.result == 'success' && (needs.reuse.result != 'success' || needs.reuse.outputs.reused != 'true') }}
runs-on: ${{ needs.free-plan.outputs.runner }}
timeout-minutes: 20
strategy:
@@ -590,13 +645,38 @@ jobs:
# Branch protection already requires the `free-tests` context. Keep that
# stable name as an always-running aggregate so every platform-specific CSO
# gate is merge-blocking without a separate branch-protection migration.
# Its first step picks one of two exclusive paths before any download: a
# verified reuse (main pushes only) links the reused run and passes; anything
# else takes the strict path below unchanged, which alone uploads tested-tree.
free-tests:
if: always()
needs: [free-suite, typecheck, quick-bun-default, cso-macos-launcher, macos-named-regressions, cso-windows-launcher, cso-docker-integration]
needs: [reuse, free-suite, typecheck, quick-bun-default, cso-macos-launcher, macos-named-regressions, cso-windows-launcher, cso-docker-integration]
runs-on: ubuntu-24.04
timeout-minutes: 5
steps:
- name: Take the reuse path only for a verified main-push reuse
id: route
env:
REUSE_RESULT: ${{ needs.reuse.result }}
REUSED: ${{ needs.reuse.outputs.reused }}
REUSE_RUN_ID: ${{ needs.reuse.outputs.run_id }}
REUSE_RUN_URL: ${{ needs.reuse.outputs.run_url }}
REUSE_TREE: ${{ needs.reuse.outputs.tree }}
run: |
set -eu
if [ "$REUSE_RESULT" != success ] || [ "$REUSED" != true ]; then
echo 'reused=false' >> "$GITHUB_OUTPUT"
exit 0
fi
test "$GITHUB_EVENT_NAME" = push
test "$GITHUB_REF" = refs/heads/main
case "$REUSE_RUN_ID" in ''|*[!0-9]*) echo "::error::the reuse job named no run id"; exit 1 ;; esac
case "$REUSE_TREE" in ''|*[!0-9a-f]*) echo "::error::the reuse job named no tree"; exit 1 ;; esac
test "$REUSE_RUN_URL" = "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$REUSE_RUN_ID"
echo "Reused $REUSE_RUN_URL, which passed on tree $REUSE_TREE; this push ran no suite of its own." >> "$GITHUB_STEP_SUMMARY"
echo 'reused=true' >> "$GITHUB_OUTPUT"
- name: Require the free suite, typecheck, the quick tier on the default Bun, macOS regressions, and every CSO platform gate
if: steps.route.outputs.reused != 'true'
env:
FREE_SUITE_RESULT: ${{ needs.free-suite.result }}
TYPECHECK_RESULT: ${{ needs.typecheck.result }}
@@ -615,16 +695,20 @@ jobs:
test "$CSO_WINDOWS_RESULT" = success
test "$CSO_DOCKER_RESULT" = success
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
if: steps.route.outputs.reused != 'true'
with:
persist-credentials: false
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
if: steps.route.outputs.reused != 'true'
with:
bun-version: 1.4.2
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8
if: steps.route.outputs.reused != 'true'
with:
name: free-plan
path: ${{ runner.temp }}
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8
if: steps.route.outputs.reused != 'true'
with:
pattern: free-result-*
merge-multiple: true
@@ -633,10 +717,27 @@ jobs:
# flake-ledger.jsonl). A green run is where a retried flake hides, so the
# summary runs before strict verification.
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8
if: steps.route.outputs.reused != 'true'
with:
pattern: flake-ledger-*
path: ${{ runner.temp }}/flake-ledgers
- name: Summarize flaky passes absorbed by the serial retry
if: steps.route.outputs.reused != 'true'
run: bun run scripts/test-health-report.ts flake-summary "$RUNNER_TEMP/flake-ledgers" >> "$GITHUB_STEP_SUMMARY"
- name: Require exact coverage and complete strict results
if: steps.route.outputs.reused != 'true'
run: bun run scripts/test-free-shards.ts --ci-verify "$RUNNER_TEMP/free-plan.json" --results "$RUNNER_TEMP/free-results"
# Only after strict verification passed, so a red or partial run never
# leaves a record; the reuse path never reaches here.
- name: Record the tested tree
if: steps.route.outputs.reused != 'true'
env:
PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: bun run scripts/ci-tree-reuse.ts record --plan "$RUNNER_TEMP/free-plan.json" --results "$RUNNER_TEMP/free-results" --os Linux,macOS,Windows --out "$RUNNER_TEMP/tested-tree/tree.json"
- name: Upload the tested tree
if: steps.route.outputs.reused != 'true'
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7
with:
name: tested-tree
path: ${{ runner.temp }}/tested-tree/tree.json
if-no-files-found: error
+78 -3
View File
@@ -19,6 +19,12 @@ name: Windows Free Tests
#
# Native browser qualification campaigns (Windows cookie extraction, launch
# diagnostics) are dispatch-only in native-qualification.yml.
#
# One full suite per tree (GSTA-156, docs/pregate.md#ci-reuse), as in
# free-tests.yml: the aggregate uploads `tested-tree` only after strict
# verification passes, and a push to main whose merged pull request already
# ran this workflow green on the same tree reuses that run. Pull request,
# dispatch and duration-recording runs never consume.
on:
pull_request:
@@ -50,8 +56,36 @@ permissions:
contents: read
jobs:
# Main pushes only, and the one job with actions: read; see free-tests.yml.
# A skipped or failed reuse job runs every job below as before.
reuse:
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-24.04
timeout-minutes: 5
permissions:
contents: read
actions: read
outputs:
reused: ${{ steps.find.outputs.reused }}
run_id: ${{ steps.find.outputs.run_id }}
run_url: ${{ steps.find.outputs.run_url }}
tree: ${{ steps.find.outputs.tree }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
with:
bun-version: 1.4.2
- id: find
name: Find a green pull request or dispatch run of this exact tree
env:
GH_TOKEN: ${{ github.token }}
run: bun run scripts/ci-tree-reuse.ts find --workflow windows-free-tests.yml --profile ci-reuse/windows-free-tests
windows-plan:
if: ${{ !inputs.record_durations }}
needs: reuse
if: ${{ !cancelled() && !inputs.record_durations && (needs.reuse.result != 'success' || needs.reuse.outputs.reused != 'true') }}
runs-on: ubuntu-24.04
timeout-minutes: 5
outputs:
@@ -79,7 +113,8 @@ jobs:
if-no-files-found: error
windows-free-shard:
needs: windows-plan
needs: [windows-plan, reuse]
if: ${{ !cancelled() && needs.windows-plan.result == 'success' && (needs.reuse.result != 'success' || needs.reuse.outputs.reused != 'true') }}
runs-on: windows-latest
timeout-minutes: 15
strategy:
@@ -184,11 +219,33 @@ jobs:
windows-free-tests:
if: ${{ always() && !inputs.record_durations }}
needs: [windows-plan, windows-free-shard]
needs: [reuse, windows-plan, windows-free-shard]
runs-on: ubuntu-24.04
timeout-minutes: 5
steps:
- name: Take the reuse path only for a verified main-push reuse
id: route
env:
REUSE_RESULT: ${{ needs.reuse.result }}
REUSED: ${{ needs.reuse.outputs.reused }}
REUSE_RUN_ID: ${{ needs.reuse.outputs.run_id }}
REUSE_RUN_URL: ${{ needs.reuse.outputs.run_url }}
REUSE_TREE: ${{ needs.reuse.outputs.tree }}
run: |
set -eu
if [ "$REUSE_RESULT" != success ] || [ "$REUSED" != true ]; then
echo 'reused=false' >> "$GITHUB_OUTPUT"
exit 0
fi
test "$GITHUB_EVENT_NAME" = push
test "$GITHUB_REF" = refs/heads/main
case "$REUSE_RUN_ID" in ''|*[!0-9]*) echo "::error::the reuse job named no run id"; exit 1 ;; esac
case "$REUSE_TREE" in ''|*[!0-9a-f]*) echo "::error::the reuse job named no tree"; exit 1 ;; esac
test "$REUSE_RUN_URL" = "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$REUSE_RUN_ID"
echo "Reused $REUSE_RUN_URL, which passed on tree $REUSE_TREE; this push ran no suite of its own." >> "$GITHUB_STEP_SUMMARY"
echo 'reused=true' >> "$GITHUB_OUTPUT"
- name: Require the plan and every Windows shard
if: steps.route.outputs.reused != 'true'
env:
PLAN_RESULT: ${{ needs.windows-plan.result }}
SHARD_RESULT: ${{ needs.windows-free-shard.result }}
@@ -197,22 +254,40 @@ jobs:
test "$PLAN_RESULT" = success
test "$SHARD_RESULT" = success
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
if: steps.route.outputs.reused != 'true'
with:
persist-credentials: false
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
if: steps.route.outputs.reused != 'true'
with:
bun-version: 1.4.2
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8
if: steps.route.outputs.reused != 'true'
with:
name: windows-plan
path: ${{ runner.temp }}
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8
if: steps.route.outputs.reused != 'true'
with:
pattern: windows-result-*
merge-multiple: true
path: ${{ runner.temp }}/windows-results
- name: Require exact coverage and complete strict results
if: steps.route.outputs.reused != 'true'
run: bun run scripts/test-free-shards.ts --windows-only --ci-verify "$RUNNER_TEMP/windows-plan.json" --results "$RUNNER_TEMP/windows-results"
# Only after strict verification passed; the reuse path never reaches here.
- name: Record the tested tree
if: steps.route.outputs.reused != 'true'
env:
PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: bun run scripts/ci-tree-reuse.ts record --plan "$RUNNER_TEMP/windows-plan.json" --results "$RUNNER_TEMP/windows-results" --os Windows --out "$RUNNER_TEMP/tested-tree/tree.json"
- name: Upload the tested tree
if: steps.route.outputs.reused != 'true'
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7
with:
name: tested-tree
path: ${{ runner.temp }}/tested-tree/tree.json
if-no-files-found: error
windows-record-durations:
if: ${{ inputs.record_durations }}