diff --git a/.github/workflows/gate.yml b/.github/workflows/gate.yml index a9b29f49..41de4720 100644 --- a/.github/workflows/gate.yml +++ b/.github/workflows/gate.yml @@ -5,7 +5,7 @@ # # Discipline (design doc: docs/research/ci-workflow-design.md, Aaron- # reviewed 2026-04-18; parity-swap landed round 32): -# - Runners digest-pinned (ubuntu-22.04, macos-14), not -latest. +# - Runners use -latest tags (maintainer Otto-212 discipline). # - Third-party actions SHA-pinned by full 40-char commit SHA; # trailing `# vX.Y.Z` comments for humans. # - permissions: contents: read at the workflow level; no job @@ -46,35 +46,58 @@ concurrency: jobs: build-and-test: - # Matrix is computed from `github.repository` at plan time so the - # macos-14 leg (≈10× Linux cost) only exists on contributor forks, - # not on the canonical Lucent-Financial-Group/Zeta repo. On any - # fork both legs exist; on the canonical repo only the ubuntu leg - # exists. This keeps the YAML byte-identical on both sides — no - # repo-specific variable, no second workflow file — with runtime - # differentiation driven by the built-in `github.repository` - # context. + # Matrix covers three standard GitHub-hosted runners + # (all free on public repos per the Otto-210 URL: + # https://docs.github.com/en/actions/how-tos/write-workflows/choose-where-workflows-run/choose-the-runner-for-a-job#standard-github-hosted-runners-for-public-repositories + # exact quote: "Use of the standard GitHub-hosted + # runners is free and unlimited on public + # repositories."): # - # Job-level `if:` with `matrix.*` is rejected by actionlint (the - # matrix context is not available at job-level), so the split is - # done at strategy-expansion time via `fromJSON`. The expression - # evaluates once per workflow run; each matrix leg that survives - # creates its own check status. + # - ubuntu-slim minimal Linux x64 image, + # standard runner (faster + # boot than ubuntu-24.04 for + # jobs that don't need the + # full image) + # - ubuntu-24.04 Linux x64, standard runner + # (latest GA Ubuntu LTS as of + # 2026-04-24 per GitHub docs) + # - ubuntu-24.04-arm Linux arm64, standard runner + # (no -latest rolling arm tag + # exists yet; 24.04-arm is + # current) + # - macos-26 macOS 26 Tahoe on Apple + # Silicon (arm64), standard + # runner, GA'd 2026-02-26 per + # GitHub changelog. Explicitly + # NOT macos-*-intel (that's + # the Intel family); we want + # M1 per maintainer Otto-211. # - # Rationale: maintainer 2026-04-21 "Mac is very very expensive - # to run" + "we should leave [LFG's] build as linux only if - # that's possible where a contributor fork also builds mac". - # `build-and-test (macos-14)` is NOT in the canonical repo's - # required-checks list — it was removed from branch protection - # on the same change that introduced this matrix split so PRs - # don't block on a leg that - # no longer exists there. + # Windows coverage deferred per maintainer Otto-211 + # directive ("windows will come later on both zeta + # and acehack"). When it lands, add windows-latest + # to the matrix above. + # + # Compounding discipline (maintainer Otto-212): use + # `-latest` tags where available instead of pinning + # to a version — avoids creating upgrade debt every + # time the runner image rolls forward. The `ubuntu- + # 24.04-arm` exception is because no rolling arm + # alias exists. + # + # Pricing history: earlier "Mac is very expensive" + # (2026-04-21) + Otto-164 "macOS NOT free" findings + # were INCORRECT. Otto-210 primary-source check + # confirmed standard runners free-for-public + # including macOS. See feedback_macos_is_free_on_ + # public_repos_otto_164_verification_was_wrong_* + # memory for the correction trail. name: build-and-test (${{ matrix.os }}) timeout-minutes: 45 strategy: fail-fast: false matrix: - os: ${{ fromJSON(github.repository == 'Lucent-Financial-Group/Zeta' && '["ubuntu-22.04"]' || '["ubuntu-22.04","macos-14"]') }} + os: [ubuntu-slim, ubuntu-24.04, ubuntu-24.04-arm, macos-26] runs-on: ${{ matrix.os }} steps: @@ -152,7 +175,7 @@ jobs: # elevation design (docs/research/threat-model-elevation.md). name: lint (semgrep) timeout-minutes: 10 - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - name: Checkout @@ -183,14 +206,14 @@ jobs: # See openspec/specs/static-analysis/profiles/shell.md. name: lint (shellcheck) timeout-minutes: 5 - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Run shellcheck - # shellcheck ships pre-installed on ubuntu-22.04 runners. + # shellcheck ships pre-installed on ubuntu-24.04 runners. # Scope: Zeta's own scripts under `tools/setup/` only — # `tools/lean4/.lake/packages/**` is Lean/Mathlib vendored # code not governed by Zeta standards. @@ -220,7 +243,7 @@ jobs: # github-actions.md. name: lint (actionlint) timeout-minutes: 5 - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - name: Checkout @@ -260,7 +283,7 @@ jobs: # No untrusted input used in run: — only a fixed repo path. name: lint (no empty dirs) timeout-minutes: 3 - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - name: Checkout @@ -276,7 +299,7 @@ jobs: # See openspec/specs/static-analysis/profiles/markdown.md. name: lint (markdownlint) timeout-minutes: 5 - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - name: Checkout diff --git a/.github/workflows/nightly-cross-platform.yml b/.github/workflows/nightly-cross-platform.yml deleted file mode 100644 index 9135c9b3..00000000 --- a/.github/workflows/nightly-cross-platform.yml +++ /dev/null @@ -1,111 +0,0 @@ -name: nightly-cross-platform - -# Cross-platform build confidence on a daily cadence. -# The PR-gate workflow (gate.yml) stays Linux-only — -# unambiguously free on public repos — per the -# Otto-164 pricing verification outcome (see -# docs/research/nightly-cross-platform-workflow-design.md -# and the BACKLOG "Otto-161 macOS CI enablement" -# history for the verification trace). -# -# This workflow adds Windows + macOS coverage at -# controlled cost: -# - ubuntu-22.04 standard runner; free on public repos -# (kept for matrix-parity comparison) -# - windows-2022 standard runner; free on public repos -# - macos-14 larger-runner classification; BILLED -# at $0.062/min per GitHub's billing -# docs. ~15 min run * $0.062 * 30 days -# = ~$28/month worst-case per repo. -# -# Scheduled daily (not per-PR) per the maintainer's -# Otto-209 directive: "we are going to do macos once a -# day schedued, for both repos, unless acehack is getting -# it free." The lucent-ksk parallel workflow lands via a -# separate BACKLOG row + cross-repo coordination. -# -# Rollback: delete macos-14 from the matrix (one-line -# diff), or delete this workflow file entirely. -# -# Security note: this workflow uses only first-party -# trusted context (github.repository, github.ref, -# github.event_name, runner.os, matrix.os). No user- -# authored fields (issue title, PR body, commit -# message, head_ref, etc.) are referenced. - -on: - schedule: - - cron: '0 9 * * *' - workflow_dispatch: {} - # Also run on PR when this workflow file itself changes - # so workflow edits get exercised before landing. - pull_request: - paths: - - '.github/workflows/nightly-cross-platform.yml' - -permissions: - contents: read - -concurrency: - group: nightly-cross-platform-${{ github.ref }} - # Nightly runs can safely supersede each other if a - # manual-dispatch queues during a scheduled run; the - # opposite of gate.yml's PR-gate semantics. - cancel-in-progress: true - -jobs: - build-and-test: - # Fork-scoping: scheduled runs ONLY fire on the - # canonical repo. On any fork, scheduled trigger - # evaluates false to avoid burning fork-owner minutes. - # Manual dispatch + PR trigger on the workflow file - # still work on forks so contributors can opt in. - if: >- - github.repository == 'Lucent-Financial-Group/Zeta' - || github.event_name == 'workflow_dispatch' - || github.event_name == 'pull_request' - name: build-and-test (${{ matrix.os }}) - timeout-minutes: 60 - strategy: - fail-fast: false - matrix: - os: [ubuntu-22.04, windows-2022, macos-14] - runs-on: ${{ matrix.os }} - - steps: - - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - - name: Cache .NET SDK - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 - with: - path: ~/.dotnet - key: dotnet-${{ runner.os }}-${{ hashFiles('global.json', 'tools/setup/common/dotnet.sh') }} - - - name: Cache NuGet packages - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 - with: - path: | - ~/.nuget/packages - ~/.local/share/NuGet - key: nuget-${{ runner.os }}-${{ hashFiles('Directory.Packages.props') }} - - - name: Install toolchain (GOVERNANCE §24 single source) - shell: bash - run: ./tools/setup/install.sh - - - name: Build (0 Warning(s) / 0 Error(s) required) - shell: bash - run: dotnet build Zeta.sln -c Release - - - name: Test - shell: bash - run: dotnet test Zeta.sln -c Release --no-build --logger "trx;LogFileName=test-results-${{ matrix.os }}.trx" --results-directory ./test-results - - - name: Upload test results - if: always() - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - with: - name: test-results-${{ matrix.os }} - path: ./test-results/*.trx - retention-days: 7