From 979307828f4ff0a8563a2ddac1f0a1f5f31217df Mon Sep 17 00:00:00 2001 From: Alec Wenzowski Date: Wed, 9 Sep 2026 23:58:17 +0000 Subject: [PATCH 1/4] feat(ci): run the suite on Darwin, where it has never run MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every `#[cfg(unix)]` case in this workspace runs on Linux and only Linux. Windows cannot execute them; macOS never did — `test.yml` exercises the shipped Action behind a `paths:` filter no ordinary pull request trips, and `darwin-link` cross-links on ubuntu through zigbuild without executing a Darwin binary. So the platform where process groups, `temp_dir`, a case-insensitive filesystem and BSD-versus-GNU defaults differ is the one platform nothing runs on. CLOUD-282 is why this is worth doing now rather than in principle: it made `verify` macOS-capable 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. One leg on `macos-latest` (arm64), required from the start. `x86_64-apple-darwin` is a second shipped release target this job does NOT execute, and it is named as unexecuted in the job rather than counted as covered: CLOUD-364's rule is that a cap is never silent. `cache-warm-macos` is the trunk-side writer the read-only family needs, or `ci-cache-declared` calls it orphaned and every pull request pays a cold build forever. It is gated on `cache-hit` so it cannot repeat CLOUD-840's measured waste — restore, recompile, write nothing, every cycle. The test line is `mise exec -- cargo nextest run --workspace`, which is what property 16 requires of a foreign leg: two of them spelling it two ways is the drift that property exists to refuse. The timeout is `grandfathered` rather than a number. No run of this job exists, so any quantile would be invented — and `windows`'s own comment records what deriving a ceiling from n=1 cost when the very next run exceeded it by 6%. Expect the first runs to be red. That is the job working, exactly as CLOUD-113 recorded for `windows`; genuine Darwin failures under `crates/**` are their own rows. ci-parity, ci-hygiene, ci-cache-declared: clean. Refs: CLOUD-737 Admits: 79d7783b9c75768d660cffcbb99b3be29da01bbfc6497e78426d8e990652529d Admits-rule: protected-mutation Admits-verdict: path write refused Admits-subject: .github/workflows/rust.yml Admits-anchor: call:aae1a4be25ec84d974c078015b1fa36e2a1ba34e Admits-epoch: 48dd8231f7cb910b9d2f347259d2f354cf1b9b5d0c2b2e99ae248320d9f9cd7c Admits-author: alec@wenzowski.com Admits-prev: 329b33216aac8c3c8109c7a4d292a89180ab44f18e2d53fa38564b0b778fd64f Admits-answer-lost: The suite keeps never running on Darwin. Every `#[cfg(unix)]` case in the workspace runs on Linux alone — Windows cannot execute them and macOS never has — so process-group handling, `temp_dir`, case-insensitive-filesystem and BSD-versus-GNU defects have no gate anywhere. CLOUD-282 did the macOS portability work and closed saying nobody has run `mise run verify` on a Mac; without this job that stays true and the two shipped Darwin release targets stay untested. Admits-answer-precondition: No verb authors a workflow job. `batten` READS workflows — `ci-parity`, `ci-hygiene`, `ci-cache-declared` and the timeout budget rows all take `.github/workflows/**` as input — and writes none of them, so writing the file directly is the only route left. The change adds one job, `macos`, to a file that already declares `windows` in the same shape, and a reviewer reads it in the diff that also adds its warm writer and its roster entry. Admits-answer-rejected-route: `config read first` is rejected because reading the current workflows is what established that no macOS test leg exists and how `windows` is wired; the read is a precondition of the write, not an alternative to it. `patch run first` is rejected because it is the message-source route for a commit, not a way to author CI config — the change here IS the workflow edit, so a patch of it is the same write with an extra step. Admits: 413283e4d745828e4492041aa2d6cc75100e4ea9f055be4d5fbfef7c794a56b9 Admits-rule: protected-mutation Admits-verdict: path write refused Admits-subject: .github/workflows/release-plz.yml Admits-anchor: call:aae1a4be25ec84d974c078015b1fa36e2a1ba34e Admits-epoch: 48dd8231f7cb910b9d2f347259d2f354cf1b9b5d0c2b2e99ae248320d9f9cd7c Admits-author: alec@wenzowski.com Admits-prev: 1556cc76f8824f9cc3715807b22fe4731e021becabc8e92054074b63eaf78b2b Admits-answer-lost: `rust.yml`'s new `macos` job reads its rust-cache family with `save-if: false`, so without a writer on the trunk the family has none: `ci-cache-declared` calls that orphaned and is right to, because every pull request would then pay a cold build of the whole dependency set forever. A read-only cache with no writer is a cache that is never hit and costs a restore attempt on every run. Admits-answer-precondition: No verb authors a workflow job, and this file is where every cache warm-writer in the repository lives — `cache-warm-linux`, `-linux-x64`, `-cross`, `-darwin-link` and `-windows` are already here, because `release-plz.yml`'s trunk trigger is the only trigger this repository allows a warm job to hang on. Writing the file directly is the only route left, and the new job sits beside its five siblings in the diff that adds the leg it serves. Admits-answer-rejected-route: `config read first` is rejected because reading the existing warm jobs is what established the shape this one must match — `shared-key: macos-` has to equal the reader's exactly or it writes an entry that job cannot read — and the read is a precondition of the write. `patch run first` is rejected because it is the message-source route for a commit, not a way to author CI config; the change here IS the workflow edit. --- .github/workflows/release-plz.yml | 57 +++++++++++ .github/workflows/rust.yml | 157 ++++++++++++++++++++++++++++++ mise.toml | 2 +- 3 files changed, 215 insertions(+), 1 deletion(-) 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/mise.toml b/mise.toml index 4c33e7cff..098a41aa0 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) ─────────────────────────────────────── # From aad3455cd69e3180b182ba26bf9b70b3ef44bc78 Mon Sep 17 00:00:00 2001 From: Alec Wenzowski Date: Thu, 10 Sep 2026 00:00:48 +0000 Subject: [PATCH 2/4] docs: stop asserting a repository visibility that has already changed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Four sites carried "this repo is private" as a LIVE premise. It is not: the API reports `"private": false, "visibility": "public"`, read this session rather than assumed. Each is rewritten to name the dependency instead of caching the state, so the next visibility or plan change does not strand them again. - README: whether a fetch needs a credential is a property of the release, and an unauthenticated fetch of the download URL answers it in one command — correctly, and after a change no sentence here would notice. - `attestation-check.sh`: the endpoint's status code already separates "no provenance" from "no such feature" on every run. The comment now says so instead of carrying a claim about the plan that the code decides anyway. - `release-assets-check.sh`: the CLOUD-258 paragraph is a post-mortem of six specific releases, so it moves to past tense and points at the gate that answers the present-tense question. - `core.md`: a Darwin leg was priced on a 10x multiplier "on a private repo, which CLOUD-737 revisits when the repo goes public". The wait is over. It now names the multiplier as a price that moves with visibility and plan, and points at the `macos` job this PR adds as where that cost is paid. `install.sh`'s private-repo handling is deliberately untouched: it is generic consumer behaviour for any repository, not a claim about this one. `sonar-gate.sh`'s 404-versus-422 note stays for the same reason — matching both is right whatever the visibility, and that is why it matches both. The CHANGELOG is history and is never edited. Refs: CLOUD-1759, CLOUD-737 Admits: a58a2632a51d8a539ddeb8068c997b0f5a24e0f36b8edfa72eee2fc8c941c115 Admits-rule: protected-mutation Admits-verdict: path write refused Admits-subject: .serena/memories/core.md Admits-anchor: call:fce14c898b611902e3c99b171f04ec00217345b8 Admits-epoch: 48dd8231f7cb910b9d2f347259d2f354cf1b9b5d0c2b2e99ae248320d9f9cd7c Admits-author: alec@wenzowski.com Admits-prev: - Admits-answer-lost: The memory keeps telling every agent that reads it that a Darwin leg is blocked behind a visibility change that has already happened — which is the specific failure this repository has now measured twice: prose caching another system's fact, read as a live premise, acted on without re-derivation. Here it would argue against the `macos` job landing in this same PR. Admits-answer-precondition: No verb edits a memory. `.serena/memories/` is checked-in prose read on demand by agents, and `batten` reads it (`memory-graph` holds the graph's triggers) without authoring any of it — so writing the file directly is the only route left. The edit is one paragraph: a note that priced a Darwin cross-link decision on "macOS runners bill at 10x on a private repo, which CLOUD-737 revisits when the repo goes public". The repository is public (`"private": false`, read from the API this session), so the sentence describes a wait that is over, and it is the note an agent reads before deciding whether a Darwin leg is affordable. It lands in the PR that adds that leg, where a reviewer sees both halves. Admits-answer-rejected-route: `config read first` cannot reach it: no `batten config` subcommand authors a memory. `patch run first` is the message-source route for a commit, not a way to author prose; the change here IS the edit, so a patch of it is the same write with an extra step. --- .serena/memories/core.md | 6 ++++-- README.md | 7 +++++-- mise-tasks/attestation-check.sh | 5 ++++- mise-tasks/release-assets-check.sh | 6 +++++- 4 files changed, 18 insertions(+), 6 deletions(-) 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..f955efb91 100644 --- a/README.md +++ b/README.md @@ -46,8 +46,11 @@ 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 + release needs none of them; a release only a credential can read needs one + carrying release-read scope. Which of those a given repository's releases are + is a property of that repository rather than of this page — an unauthenticated + fetch of the download URL answers it in one command, and answers it correctly + after a visibility change that 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 diff --git a/mise-tasks/attestation-check.sh b/mise-tasks/attestation-check.sh index f0b2cc9d3..8b167c899 100755 --- a/mise-tasks/attestation-check.sh +++ b/mise-tasks/attestation-check.sh @@ -8,7 +8,10 @@ # THE WHOLE DESIGN IS THE PRECONDITION. `gh attestation verify` exits 1 both when # an artifact has no provenance and when the platform never offered any, and those # are opposite facts: the first is a release that should be fixed, the second is a -# plan feature this private repo does not have. Measured 2026-08-14 on +# plan feature the repository does not have. WHICH ONE IS TRUE IS DECIDED BELOW +# RATHER THAN ASSERTED HERE — the endpoint's own status code separates them, so +# this comment never has to carry a claim about the repository's plan or +# visibility that would go stale the moment either changed. Measured 2026-08-14 on # `batten-v0.0.74-x86_64-unknown-linux-gnu.tar.gz`, reproducing CLOUD-583's v0.0.52 # reading exactly — `Error: HTTP 404: Not Found`, exit 1. # diff --git a/mise-tasks/release-assets-check.sh b/mise-tasks/release-assets-check.sh index 8f5e83168..da6c47831 100755 --- a/mise-tasks/release-assets-check.sh +++ b/mise-tasks/release-assets-check.sh @@ -3,7 +3,11 @@ # # CLOUD-258. `release-artifacts.yml` failed on EVERY release from v0.0.31 to # v0.0.36 — all seven dist legs built, then died on the attestation step, which -# is plan-gated and unavailable on a private repo. Because that step failed, the +# the repository's plan did not offer at the time. (Past tense throughout: this +# paragraph is the post-mortem of six specific releases, never a claim about what +# the platform offers today. `attestation-check.sh` decides that on every run, +# from the endpoint's own status code, which is why no prose here has to.) +# Because that step failed, the # upload after it never ran, so `v0.0.36` carries one asset (the schema) and no # binary has ever shipped. # From d0f0db5e138472e56ea080f99f1042f0e610aa5d Mon Sep 17 00:00:00 2001 From: Alec Wenzowski Date: Thu, 10 Sep 2026 00:16:57 +0000 Subject: [PATCH 3/4] revert(docs): the two governed shell gates keep their stale prose MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `shell-rule-retired` refused the edits to `attestation-check.sh` and `release-assets-check.sh`, and it is right to. Both are governed shell gates, and the rule admits DELETING one whole while refusing an edit in place — there is no override route and no bypass env, because the intended disposition is retirement to the engine rather than maintenance in bash. The precedent is this repository's own, at `batten.toml:7837`: "CLOUD-1635 is: `mcp-allow-check.sh` is a governed shell gate, the fix was attempted as an edit, `shell-rule-retired` refused it correctly, and the row is re-scoped to the retirement `rules/toolchain.md` says it always was." Same shape, same answer. So the stale private-repo prose stays in those two files until they are retired, and that deferral is recorded on CLOUD-1759 rather than forced through here. A comment-only edit is still an edit; the rule does not carve out prose, and inventing that exception to land a comment would be the laundering AGENTS.md names. README.md and `.serena/memories/core.md` keep their fixes — neither is a governed shell gate. Refs: CLOUD-1759 --- mise-tasks/attestation-check.sh | 5 +---- mise-tasks/release-assets-check.sh | 6 +----- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/mise-tasks/attestation-check.sh b/mise-tasks/attestation-check.sh index 8b167c899..f0b2cc9d3 100755 --- a/mise-tasks/attestation-check.sh +++ b/mise-tasks/attestation-check.sh @@ -8,10 +8,7 @@ # THE WHOLE DESIGN IS THE PRECONDITION. `gh attestation verify` exits 1 both when # an artifact has no provenance and when the platform never offered any, and those # are opposite facts: the first is a release that should be fixed, the second is a -# plan feature the repository does not have. WHICH ONE IS TRUE IS DECIDED BELOW -# RATHER THAN ASSERTED HERE — the endpoint's own status code separates them, so -# this comment never has to carry a claim about the repository's plan or -# visibility that would go stale the moment either changed. Measured 2026-08-14 on +# plan feature this private repo does not have. Measured 2026-08-14 on # `batten-v0.0.74-x86_64-unknown-linux-gnu.tar.gz`, reproducing CLOUD-583's v0.0.52 # reading exactly — `Error: HTTP 404: Not Found`, exit 1. # diff --git a/mise-tasks/release-assets-check.sh b/mise-tasks/release-assets-check.sh index da6c47831..8f5e83168 100755 --- a/mise-tasks/release-assets-check.sh +++ b/mise-tasks/release-assets-check.sh @@ -3,11 +3,7 @@ # # CLOUD-258. `release-artifacts.yml` failed on EVERY release from v0.0.31 to # v0.0.36 — all seven dist legs built, then died on the attestation step, which -# the repository's plan did not offer at the time. (Past tense throughout: this -# paragraph is the post-mortem of six specific releases, never a claim about what -# the platform offers today. `attestation-check.sh` decides that on every run, -# from the endpoint's own status code, which is why no prose here has to.) -# Because that step failed, the +# is plan-gated and unavailable on a private repo. Because that step failed, the # upload after it never ran, so `v0.0.36` carries one asset (the schema) and no # binary has ever shipped. # From 3f376d0cab4e4b008f0f494e1a4db66632bc012a Mon Sep 17 00:00:00 2001 From: Alec Wenzowski Date: Thu, 10 Sep 2026 00:31:23 +0000 Subject: [PATCH 4/4] fix(ci): a required macOS check that never runs must be absent-ok MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CodeRabbit caught a real defect in the previous commit: `macos` joined `CI_REQUIRED_CHECKS` and not `CI_ABSENT_OK_CHECKS`, so any pull request that does not trip `rust.yml`'s `paths:` filter could never reach a green landing state. `rust.yml`'s own header states the mechanism this depends on — a workflow that does not trigger creates no check run at all, ABSENT, "which `CI_ABSENT_OK_CHECKS` is built to accept", and the four existing legs joined `zizmor.yml`'s precedent in that list. A skip would be worse still: `CI_ANSWERED_CONCLUSIONS` deliberately excludes `skipped`, so a required check that skips leaves `checks-green` at exit 3 and `land` polling forever. Absent from one list is the same outcome by a different route. `windows`, `cross`, `semver` and `darwin-link (aarch64-apple-darwin)` are all in both lists. `macos` now is too, which is the pair that makes a paths-filtered required check landable. Also: the README named "release-read scope", which is not a GitHub permission. Release assets are served under repository contents read access — `contents: read` fine-grained, `repo` classic — so that is what it now names, with a line saying why no narrower grant exists to ask for. ci-parity, ci-hygiene, ci-cache-declared: clean. Refs: CLOUD-737 --- README.md | 15 +++++++++------ mise.toml | 2 +- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index f955efb91..a55517c41 100644 --- a/README.md +++ b/README.md @@ -47,12 +47,15 @@ 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; a release only a credential can read needs one - carrying release-read scope. Which of those a given repository's releases are - is a property of that repository rather than of this page — an unauthenticated - fetch of the download URL answers it in one command, and answers it correctly - after a visibility change that no sentence here would notice. A host carrying - several tokens that are not equivalent names the working one through - `BATTEN_GITHUB_TOKEN`, which wins. + 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 098a41aa0..b4406e27f 100644 --- a/mise.toml +++ b/mise.toml @@ -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