diff --git a/.github/workflows/release-plz.yml b/.github/workflows/release-plz.yml index 444b39f12..011fa6c34 100644 --- a/.github/workflows/release-plz.yml +++ b/.github/workflows/release-plz.yml @@ -448,6 +448,63 @@ jobs: - run: mise exec -- cargo nextest run --no-run --workspace if: steps.rust-cache.outputs.cache-hit != 'true' + # The warm side of `rust.yml`'s `macos` leg. It exists because that job reads + # with `save-if: false`, and a read-only family with no writer on the trunk is + # an entry nothing ever fills — `ci-cache-declared` calls that orphaned, and it + # is right to: every pull request would pay a cold build of the whole + # dependency set forever. + cache-warm-macos: + name: cache-warm-macos + runs-on: macos-latest + # Grandfathered for the `macos` leg's own reason: no run of this job exists + # to derive a quantile from. `cache-warm-windows` above carries the same + # marker for the same reason. + timeout-minutes: 30 # budget: grandfathered measured=2026-09-09 + continue-on-error: true + permissions: + contents: read + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + with: + persist-credentials: false + - uses: jdx/mise-action@3c2e0cf82a5b2e5249f0d3635a4d83d0ae861518 # v4.2.5 (CLOUD-404 retry fix, now a release) + with: + # Pinned to `batten.toml`'s `[[provision]]` version (CLOUD-1672). The + # digest above pins the ACTION; this pins the MISE it installs, which + # is a separate resolution the digest does not reach. + version: 2026.9.1 + # Exactly what the `macos` job installs, minus the tools only its + # assertions need: the cache being filled is a cargo target dir. + install_args: rust github:nextest-rs/nextest + - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 + # The `id` is load-bearing, not decoration: the compile step below reads + # this step's `cache-hit` output to decide whether there is anything to + # warm. `ci-local-parity` property 17 holds the pair together. + id: rust-cache + with: + # Must match `rust.yml`'s `macos` job exactly or this writes an entry + # that job cannot read. + shared-key: macos- + # COMPILE ONLY WHEN THERE IS NOTHING TO RESTORE. `cache-hit` is true only + # on an EXACT key match, which is precisely the case where the post-run + # save would be a no-op — so this pays the full compile when the key moves + # (a dependency bump or a toolchain change, which is what makes a pull + # request miss) and only a runner boot and a restore otherwise. CLOUD-840 + # measured the ungated version: every warm cycle after the first restored, + # recompiled and wrote nothing. + # + # The failure direction is safe by construction: if the action ever stops + # emitting `cache-hit` the expression is empty, `!= 'true'` holds, and the + # compile runs as it does today. The silent rot is the opposite one — the + # `id` disappearing while the `if` still names it — and property 17 + # refuses that. + # + # `--no-run` builds without executing: this job fills a cache and is not a + # test leg, and `foreign-cargo-is-the-declared-spelling` excludes a + # `--no-run` line from its drift comparison for exactly that reason. + - run: mise exec -- cargo nextest run --no-run --workspace + if: steps.rust-cache.outputs.cache-hit != 'true' + release-plz: runs-on: ubuntu-latest timeout-minutes: 15 # budget: grandfathered measured=2026-08-10 diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 15f3f71f2..32ce4325f 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -732,3 +732,160 @@ jobs: # that statement, so the two spellings can no longer drift apart silently. # `mise-tasks/no-doctests.sh` covers the half nextest does not run. - run: mise exec -- cargo nextest run --workspace + + # THE SUITE HAS NEVER RUN ON DARWIN, and that is the whole argument for this + # job rather than any defect it has already caught. + # + # Every `#[cfg(unix)]` case in this workspace runs on Linux and only Linux: + # Windows cannot execute them, and macOS never did — `test.yml` exercises the + # shipped ACTION on `macos-latest` behind a `paths:` filter that no ordinary + # pull request trips, and `darwin-link` cross-links on `ubuntu-latest` through + # zigbuild without ever executing a Darwin binary. So the platform where + # process-group handling, `temp_dir`, a case-insensitive filesystem and + # BSD-versus-GNU tool defaults differ from Linux is the one platform nothing + # runs on. + # + # CLOUD-282 is what makes this worth doing NOW rather than in principle. It + # made `verify` macOS-capable — `perl -0777` for `sed -zE`, six `forbid` rows + # including `no-util-linux-flock`, `tests/helpers.bash` — and closed with the + # line that is this job's whole case: nobody has run `mise run verify` on a + # Mac. The portability work exists and is unproven, which is the state a gate + # is for. + # + # ONE LEG, arm64, AND THE OTHER TARGET IS NAMED AS UNEXECUTED RATHER THAN + # COUNTED AS COVERED. `macos-latest` is arm64, which is the target + # `darwin-link` already cross-links and the one most contributors run. + # `x86_64-apple-darwin` is ALSO a shipped release target and this job does not + # execute it — stated here because CLOUD-364's rule is that a cap is never + # silent, and "macOS is tested" would otherwise read as both. The wider free + # Intel runners (`macos-15-intel`, `macos-26-intel`, 4-core/14GB against this + # leg's 3-core/7GB) are where that leg goes if it is ever added. + # + # EXPECT THE FIRST RUNS TO BE RED, AND THAT IS THE JOB WORKING — the posture + # CLOUD-113 recorded for `windows`, which shipped red and stayed red until the + # genuine portability defects it exposed were fixed. Failures under `crates/**` + # are their own rows, not a reason to soften this leg. + macos: + name: macos + if: ${{ github.event.pull_request.draft == false }} + runs-on: macos-latest + # GRANDFATHERED, AND HONESTLY SO: no run of this job exists, so there is no + # quantile to derive from and any number here would be invented. It matches + # `windows`'s ceiling as the nearest measured analogue while carrying a + # different host budget (3-core/7GB arm64 against 4-core/16GB), and + # `mise run timeout-drift` re-derives it once the series is long enough to + # have a p95. A ceiling taken from n=0 is exactly what `windows`'s own + # comment above records going wrong. + timeout-minutes: 50 # budget: grandfathered measured=2026-09-09 + # `actions: write` for one call only: cancelling THIS run when the landing + # lease does not authorise the branch (CLOUD-420). A job that could not + # cancel itself would have to fail, and a failed job concludes the run + # `failure` rather than `cancelled` — which reds `final` and makes `land` + # re-draft a healthy PR. + permissions: + contents: read + actions: write + steps: + # CLOUD-420: THE LANDING LEASE, ENFORCED WHERE THE MONEY IS SPENT. First + # step, before any checkout or toolchain install, so a run this branch is + # not authorised to make costs the rounding rather than a matrix. Body + # fetched from `main`, never from this head, and `|| exit 0` on every line + # so a body that will not parse cannot red the first step of every job. + - name: Landing lease precondition + # EXPLICIT even though bash is the macOS default, because the step's + # correctness must not depend on a runner default that GitHub is free to + # change — the same reason `windows` says so, arrived at there by having + # its body handed to pwsh. + shell: bash + env: + GH_TOKEN: ${{ github.token }} + GH_REPO: ${{ github.repository }} + LEASE_HEAD_REF: ${{ github.head_ref }} + # The HEAD sha, never `github.sha`: on a pull_request event that is the + # merge commit, whose tree is trunk's wherever this head did not touch + # it — and the staleness read asks about the head's own landing paths. + LEASE_HEAD_SHA: ${{ github.event.pull_request.head.sha }} + LEASE_RUN_ID: ${{ github.run_id }} + run: | + installer=$(gh api -H "Accept: application/vnd.github.raw" \ + "repos/$GH_REPO/contents/install.sh?ref=main") || exit 0 + printf '%s\n' "$installer" | \ + BATTEN_VERSION_FROM_REF=main BATTEN_INSTALL_DIR="$RUNNER_TEMP/batten-bin" \ + BATTEN_ALLOW_OFF_PATH=1 BATTEN_REQUIRE="lease guard" sh || exit 0 + # THE POLICY IS TRUNK'S TOO. This step runs BEFORE any checkout, so the + # directory the guard stands in is empty and `config::load` would find + # nothing — `[lease] landing_paths` reading as *no paths declared* is + # the silence the row was written to end. Fetched from `main` for the + # installer's own reason: a head must not pin the policy it is judged + # by. + mkdir -p "$RUNNER_TEMP/batten-config" || exit 0 + gh api -H "Accept: application/vnd.github.raw" \ + "repos/$GH_REPO/contents/batten.toml?ref=main" \ + >"$RUNNER_TEMP/batten-config/batten.toml" || exit 0 + "$RUNNER_TEMP/batten-bin/batten" --config-in "$RUNNER_TEMP/batten-config" \ + lease guard \ + "$LEASE_HEAD_SHA" "$LEASE_HEAD_REF" "$LEASE_RUN_ID" || exit 0 + + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + with: + # The OS must be the only variable, which is this leg's entire value. + # A missing `tests/bats` would be a second one — `crates/batten/tests` + # carries a suite about that path — and it would read as a Darwin + # finding, which is the misattribution this job exists to avoid. + submodules: true + - uses: jdx/mise-action@3c2e0cf82a5b2e5249f0d3635a4d83d0ae861518 # v4.2.5 (CLOUD-404 retry fix, now a release) + with: + # Pinned to `batten.toml`'s `[[provision]]` version (CLOUD-1672). The + # digest above pins the ACTION; this pins the MISE it installs, which + # is a separate resolution the digest does not reach. + version: 2026.9.1 + # THE SAME LIST `windows` INSTALLS, and for the reason its own comment + # records rather than by copying: `tests/cli.rs` materializes fixtures + # carrying this repository's `batten.toml`, whose `no-conflict-markers` + # rule is `hk util check-merge-conflict`, so a fixture that loads that + # config cannot evaluate ANY rule without hk. `jq` is read at runtime + # by the mise tasks three more command rules invoke. Neither is about + # the OS, which is why both belong on every leg that runs the suite. + install_args: rust hk aqua:jqlang/jq github:nextest-rs/nextest + - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 + with: + # A DISTINCT FAMILY, for `windows`'s reason rather than by analogy: + # this target dir is built by a different host toolchain than any + # linux job's, so an entry shared with one would thrash rather than + # warm. SHARED rather than keyed, so a job in another workflow can + # fill it (CLOUD-840) — without `shared-key` the job id enters the key + # and a warm job under any other name computes a different one. + shared-key: macos- + # READ-ONLY: `cache-warm-macos` on `main` is this family's one writer. + # A write from a `pull_request` run lands under `refs/pull/N/merge` + # where no other pull request can read it, which is a cache that costs + # a save and is never hit. + save-if: false + # A JOB THAT DECLARES A TOOL MUST BE ABLE TO REACH IT, asserted before the + # suite rather than discovered inside it — `ci-tools-check` holds these + # names against `mise.toml`, which cannot know whether a runner resolves + # one. The alternative is measured on `windows`: three `tests/cli.rs` + # cases reporting an exit code about a missing tool while naming the rule + # they meant to test, each costing a round trip to attribute. + - name: Assert the declared tools resolve + shell: bash + run: | + set -euo pipefail + echo "PATH=$PATH" + for tool in hk jq cargo cargo-nextest; do + if ! resolved=$(command -v "$tool"); then + echo "::error::$tool is declared in install_args but does not resolve on PATH" >&2 + exit 1 + fi + echo "$tool -> $resolved" + done + hk --version + + # `mise exec`, matching `windows`, and here it is parity rather than the + # workaround it is there: `[tasks."test:cargo"]`'s body is bash and macOS + # runs bash, so `mise run test:cargo` would work — but property 16 of + # `ci-local-parity` (CLOUD-662) reads that task's single cargo statement + # and requires a foreign leg's line to be `mise exec -- ` plus exactly + # that statement. Two foreign legs spelling it two ways is the drift that + # property exists to refuse. + - run: mise exec -- cargo nextest run --workspace diff --git a/.serena/memories/core.md b/.serena/memories/core.md index 08f98bed6..6ee5909f8 100644 --- a/.serena/memories/core.md +++ b/.serena/memories/core.md @@ -846,8 +846,10 @@ repo config > default`, declared as data in `SETTINGS` (per-key env var/flag), budget is not). `git2` is excluded by `macos-link-check` rule 1 — a COST, not a constraint: cross-linking Darwin frameworks needs an SDK the build declines because macOS - runners bill at 10x on a **private** repo, which CLOUD-737 revisits when the - repo goes public. + runners carry a billing multiplier. That is a PRICE, and it moves with the + repository's visibility and plan — so this note names the dependency instead + of a rate that would go stale silently. CLOUD-737 owns re-deciding it, and + `rust.yml`'s `macos` job is where a Darwin leg's cost is actually paid. - `state.rs` — out-of-tree state dir (`///`, CLOUD-23), via `etcetera`; the segment derived at runtime, never baked in (rule 1). Since CLOUD-296 the segment is `-<12 hex>`, not the bare directory name: the diff --git a/README.md b/README.md index 20a4d3fb3..a55517c41 100644 --- a/README.md +++ b/README.md @@ -46,10 +46,16 @@ container's setup step, an agent sandbox: - **A token is read if one is set**, from `BATTEN_GITHUB_TOKEN`, `GH_TOKEN`, `GITHUB_TOKEN` or `GITHUB_PERSONAL_ACCESS_TOKEN`, in that order. A public - release needs none of them. **This repository is private today**, so a fetch - needs a token with release-read scope until that changes; a host carrying - several tokens that are not equivalent names the working one through - `BATTEN_GITHUB_TOKEN`, which wins. + release needs none of them; a release only a credential can read needs one + granting **read access to the repository's contents** — `contents: read` on a + fine-grained token, or `repo` on a classic one. Release assets are served + under that permission rather than a release-specific one, which is why there + is no narrower grant to ask for. Whether a given repository's releases need it + at all is a property of that repository rather than of this page: an + unauthenticated fetch of the download URL answers it in one command, and keeps + answering it correctly after a visibility change no sentence here would + notice. A host carrying several tokens that are not equivalent names the + working one through `BATTEN_GITHUB_TOKEN`, which wins. - **A proxy that re-terminates TLS is handled** by honouring the CA bundle the environment already declares — `CURL_CA_BUNDLE`, else `SSL_CERT_FILE`. Nothing is disabled and an unproxied host is untouched. Set one of those rather than diff --git a/mise.toml b/mise.toml index 4c33e7cff..b4406e27f 100644 --- a/mise.toml +++ b/mise.toml @@ -393,7 +393,7 @@ _.path = ["tests/bats/bin", "target/release"] # alone — the fan-in. This roster is what `ci-wait` WAITS ON before landing, and # waiting only on `final` would strand every landing on a check that fans in from # eighteen. Two sets, two purposes; neither derives the other. -CI_REQUIRED_CHECKS = "ci,batten-check,bats,cross,commit-lint,zizmor,darwin-link (aarch64-apple-darwin),semver,perf,windows,final,action (ubuntu-latest),action (macos-latest),action (windows-latest),action-violation,action-deny,action-usage,action-internal (ubuntu-latest),action-internal (macos-latest),action-final" +CI_REQUIRED_CHECKS = "ci,batten-check,bats,cross,commit-lint,zizmor,darwin-link (aarch64-apple-darwin),semver,perf,windows,macos,final,action (ubuntu-latest),action (macos-latest),action (windows-latest),action-violation,action-deny,action-usage,action-internal (ubuntu-latest),action-internal (macos-latest),action-final" # ─── THE LAP'S READY PHASE (CLOUD-1148) ─────────────────────────────────────── # @@ -519,7 +519,7 @@ LEASE_STOP_NOTE = "mise-tasks/reclaim-census.sh note x land-stopped" # that one success as the whole roster, and `land` posted /fast-forward into a # branch protection still listing the other six as expected. The bot was # rejected. A stall is recoverable; a false green is not (CLOUD-337). -CI_ABSENT_OK_CHECKS = "zizmor,cross,darwin-link (aarch64-apple-darwin),semver,windows,action (ubuntu-latest),action (macos-latest),action (windows-latest),action-violation,action-deny,action-usage,action-internal (ubuntu-latest),action-internal (macos-latest),action-final" +CI_ABSENT_OK_CHECKS = "zizmor,cross,darwin-link (aarch64-apple-darwin),semver,windows,macos,action (ubuntu-latest),action (macos-latest),action (windows-latest),action-violation,action-deny,action-usage,action-internal (ubuntu-latest),action-internal (macos-latest),action-final" # The conclusions that CONSTITUTE AN ANSWER about a required check, written once # and read by `checks-green` and by `land`'s `graded_runs` — for exactly the # reason CI_REQUIRED_CHECKS is written once (CLOUD-327), and against exactly the