Skip to content
zit Artifact [8f3c1cb998]
Login

Artifact [8f3c1cb998]

Artifact 8f3c1cb998cbb7e2bf1731bf728b8f435cc7ad330b32e887af7942548922dd9c:


//! Structural pins over the GitHub Actions workflow that gates this repository.
//!
//! The pipeline itself is the real test — a green run against a real runner is
//! the only thing that proves fossil installs, Zig builds, and smoke can spawn
//! a server. What a green run cannot prove is that the workflow still invokes
//! everything it is supposed to invoke: a build step added to build.zig months
//! from now is silently *not* run by CI, and CI stays green while covering
//! less. That is the failure this module exists to catch, and it is a
//! repository-file fact, so it is pinned the same way src/packaging_test.zig
//! pins the shipped distribution: embed the file, assert on its text, let
//! `zig build test` be the thing that notices.
//!
//! The workflow arrives through build.zig's `build_unit_test_module` as the
//! anonymous import `ci_workflow`, deliberately on the test-only module: the
//! exported `zit` module must never depend on a repo-root file outside
//! `build.zig.zon`'s `.paths`, or a consumer fetching the package fails before
//! compiling a line. `.github` is allowlisted in packaging_test.zig's
//! `test_only_roots` for exactly that reason.
//!
//! That allowlist is a *packaging* exemption only. The workflow file still has
//! to be version-controlled, or a clean checkout fails to build on the embed —
//! and because it lives under a dot-directory, bare `fossil extras` never
//! reports it as untracked (`fossil extras --dotfiles` does). That near-miss is
//! why src/packaging_test.zig now pins tracking separately from packaging.
//!
//! These are text assertions, not a YAML parse. A pinned command sitting inside
//! a YAML comment would satisfy them. That trade is taken knowingly: a parser
//! for one file is more machinery than the risk warrants, and the workflow is
//! short enough that a human reads it whole. What the pins buy is the
//! *negative space* — the reviewed-exception list below — which no amount of
//! reading catches reliably.

const std = @import("std");

const ci_workflow = @embedFile("ci_workflow");
const release_workflow = @embedFile("release_workflow");
const build_source = @embedFile("build_source");

fn expect_contains(haystack: []const u8, needle: []const u8, where: []const u8) !void {
    if (std.mem.indexOf(u8, haystack, needle) != null) return;
    std.debug.print("\nci pin: {s} does not contain \"{s}\"\n", .{ where, needle });
    return error.CiPinMissing;
}

/// Counts non-overlapping occurrences of `needle`. Used for the single-job pin:
/// "one gating job" is a claim about how many `runs-on:` keys exist, which a
/// containment check cannot make.
fn count_occurrences(haystack: []const u8, needle: []const u8) usize {
    if (needle.len == 0) return 0;
    var seen: usize = 0;
    var scan: usize = 0;
    while (std.mem.indexOfPos(u8, haystack, scan, needle)) |hit| {
        seen += 1;
        scan = hit + needle.len;
    }
    return seen;
}

/// The exact Zig toolchain the release is built and tested with. Pinned as a
/// constant here and asserted against the workflow so the two cannot drift:
/// a runner silently upgrading Zig turns "0.1.0 builds on 0.16.0" into an
/// unverified claim.
const pinned_zig_version = "0.16.0";

/// The oldest fossil the conformance claim is stated against (#100). Not a
/// formality: `smoke` is the only lane that runs a real fossil against zit, so
/// this number is what "conforms to fossil" means for a given release, and the
/// runners must agree on it or the gate's verdict is unattributable.
const pinned_fossil_minimum = "2.28";

/// Published SHA-256 digests of the three pinned Zig toolchain tarballs
/// (ziglang.org/download/index.json, 2026-07-20). The workflow matrix verifies
/// each native target against its own digest; pinning all three here prevents a
/// platform leg from silently accepting a swapped or mismatched tarball.
const pinned_zig_shasums = [_][]const u8{
    "b23d70deaa879b5c2d486ed3316f7eaa53e84acf6fc9cc747de152450d401489",
    "70e49664a74374b48b51e6f3fdfbf437f6395d42509050588bd49abe52ba3d00",
    "ea4b09bfb22ec6f6c6ceac57ab63efb6b46e17ab08d21f69f3a48b38e1534f17",
};

/// The command that turns the digests above from recorded values into
/// enforced ones. Pinned separately because the two are independently
/// deletable: dropping this line while leaving `ZIG_SHASUM:` in the `env:`
/// block would leave a digest sitting in the file that nothing checks — the
/// same "a value appearing in the file is not the value being used" trap the
/// battery's whole-line anchoring guards against.
/// Anchored to the whole checking command, not the bare tool name: `sha256sum
/// --check --strict` appearing anywhere satisfies a loose substring while the
/// value actually fed to it is something other than the pinned `ZIG_SHASUM` —
/// a digest check that verifies the wrong digest. Naming the env var and the
/// tarball on the same line is the reverse assertion: the digest this file
/// pins is provably the digest the workflow checks.
const pinned_digest_check = "if [ \"$actual\" != \"${ZIG_SHASUM}\" ]; then";

/// The release battery, in the order the workflow runs it — asserted, not just
/// described: the pin below requires each entry to appear later in the file than
/// the one before it.
///
/// Each entry is a whole `run:` line, `run: ` prefix and trailing newline
/// included. Anchoring matters more than it looks: as a bare substring
/// `zig build` is contained in `zig build test`, `zig build e2e`, `zig build
/// dst` and `zig build smoke`, so deleting the bare-build step would have left
/// the pin passing while claiming coverage it no longer had.
///
/// `zig fmt --check build.zig src` rather than `--check .`: the bare dot walks
/// `.zig-cache/` and `zig-out/`, whose generated sources are not ours to
/// format and whose presence differs between a cold runner and a warm local
/// tree. Naming the two real source roots keeps the check meaning the same
/// thing in both places.
const battery = [_][]const u8{
    "run: zig fmt --check build.zig src\n",
    "run: zig build\n",
    "run: zig build test\n",
    "run: zig build e2e\n",
    "run: zig build dst\n",
    "run: zig build smoke\n",
    "run: zig build muscle\n",
};

/// Every step build.zig declares. Asserted as an exact set, in neither
/// direction forgiving: a step added to build.zig and a step deleted from it
/// both fail here, which is the point — the scan below is only meaningful if it
/// is looking at the step list this file was written against.
///
/// An earlier revision guarded the scan with `checked < 8`, a floor sitting
/// exactly at the current count. It fired on any legitimate step removal, said
/// nothing about *which* step went missing, and would have been satisfied by a
/// step being renamed. Naming the set costs the same edit and reports the
/// actual difference.
const declared_steps = [_][]const u8{
    "test",
    "gate",
    "run",
    "e2e",
    "git-import-e2e",
    "git-export-e2e",
    "fuzz",
    "dst",
    "bench",
    "import-git-bench",
    "smoke",
    "muscle",
    "cov",
    "git-import-refs",
    "git-import-cli",
};

/// Steps declared in build.zig that CI deliberately does not run, each with the
/// reason it is out of scope. This list is the point of the whole module: the
/// pin below fails when build.zig grows a step that is neither invoked by CI
/// nor named here, which forces the "should CI run this?" decision at the
/// moment the step is written instead of never.
/// Steps CI runs, but not as a bare `run: zig build <step>` line — the sweep
/// below drives `fuzz` one target per invocation inside a loop, so the plain
/// whole-line invocation the forward pin looks for never appears. Each entry
/// carries the exact anchor that proves the step really is invoked, so this is
/// an alternate proof of coverage, NOT an exemption from it: an entry whose
/// anchor is absent fails exactly like a missing invocation. Keeping it
/// separate from `not_in_ci` matters — conflating "runs, in a loop" with
/// "deliberately skipped" is how a step silently stops being covered while a
/// stale excuse keeps the pin green.
const ci_parameterized = [_]struct { step: []const u8, anchor: []const u8, how: []const u8 }{
    .{
        .step = "fuzz",
        .anchor = "zig build fuzz -- \"${target}\" --seed \"${FUZZ_SEED}\" --rounds \"${FUZZ_ROUNDS}\"\n",
        .how = "swept over every target enumerated from the step's own usage output, at a fixed seed and round budget",
    },
};

const not_in_ci = [_]struct { step: []const u8, why: []const u8 }{
    .{ .step = "run", .why = "developer convenience wrapper around the installed exe" },
    .{ .step = "gate", .why = "aggregate of lanes CI already invokes one by one; running it too would rebuild and rerun the same work while reporting one failure where CI currently reports which lane broke. Note this means `gate` itself is never exercised in CI -- if a lane is ever added to `gate` WITHOUT a matching `run:` line below, it will be gated locally and nowhere else" },
    .{ .step = "bench", .why = "measures throughput; a shared runner's timings are noise, and rule 7 forbids claims not backed by a real measurement" },
    .{ .step = "import-git-bench", .why = "bounded scaling qualification workload, deliberately out-of-band like the other benchmark" },
    .{ .step = "cov", .why = "out-of-band by design (needs kcov installed); never gates a commit" },
    .{ .step = "git-import-refs", .why = "`zig build test` runs this test binary; the dedicated step is only a focused developer alias" },
    .{ .step = "git-import-cli", .why = "`zig build test` runs this test binary; the dedicated step is only a focused developer alias" },
};

test "the workflow triggers on every push and on pull requests" {
    // Anchored as the whole trigger block rather than as three loose
    // substrings: bare `on:` is contained in `runs-on:`, so it asserted nothing
    // about the triggers at all.
    try expect_contains(ci_workflow, "on:\n  push:\n  pull_request:\n", ".github/workflows/ci.yml");

    // No `branches:` filter under the triggers: the release runs on
    // `release-0.1`, and a workflow that only fires for the default branch
    // would leave the entire release unverified until the merge.
    if (std.mem.indexOf(u8, ci_workflow, "branches:") != null) {
        std.debug.print("\nci pin: .github/workflows/ci.yml filters triggers by branch; " ++
            "the release branch (release-0.1) must be gated too, so the triggers stay unfiltered\n", .{});
        return error.CiPinMissing;
    }
}

test "the workflow pins every native Zig toolchain by version and digest" {
    // Target-specific digests live in the matrix; the environment assignment
    // must select the current leg's value rather than restating one platform.
    try expect_contains(ci_workflow, "ZIG_VERSION: " ++ pinned_zig_version ++ "\n", ".github/workflows/ci.yml");
    try expect_contains(ci_workflow, "ZIG_SHASUM: ${{ matrix.zig_shasum }}\n", ".github/workflows/ci.yml");
    try expect_contains(ci_workflow, "zig_shasum: " ++ pinned_zig_shasums[0] ++ "\n", ".github/workflows/ci.yml");
    try expect_contains(ci_workflow, "zig_shasum: " ++ pinned_zig_shasums[1] ++ "\n", ".github/workflows/ci.yml");
    try expect_contains(ci_workflow, "zig_shasum: " ++ pinned_zig_shasums[2] ++ "\n", ".github/workflows/ci.yml");

    // The digest is only a supply-chain guarantee if something checks it. See
    // `pinned_digest_check`.
    try expect_contains(ci_workflow, pinned_digest_check, ".github/workflows/ci.yml");
}

test "the workflow bounds how long a run may hang" {
    // The bounded-waits rule applied to CI itself. GitHub's default is 360
    // minutes, which is indistinguishable from "no timeout" in practice: a
    // hung smoke server would hold the gate open for six hours without ever
    // reporting failure.
    //
    // So the values are pinned, not merely the key. Asserting that
    // `timeout-minutes:` appears twice is satisfied by `timeout-minutes: 360`
    // in both places — the default restated, certifying neither an interval nor
    // a bound. Each pin is a whole line with its YAML indentation, the way the
    // `permissions:\n  contents: read\n` pin is: four spaces is the job
    // ceiling, eight is a step bound, so the indentation is what makes "the
    // job has a ceiling *and* smoke has a tighter one" a claim rather than a
    // count of keys.
    try expect_contains(ci_workflow, "\n    timeout-minutes: 20\n", ".github/workflows/ci.yml");

    // smoke spawns a server: it is the one step that can hang without producing
    // output, so it carries the tightest bound in the battery.
    try expect_contains(ci_workflow, "\n        timeout-minutes: 5\n", ".github/workflows/ci.yml");
}

test "the workflow takes the least privilege it can and cancels superseded runs" {
    // The battery only reads the checkout. Without an explicit block the job
    // inherits the repo/org default, which is read/write on many
    // configurations — a wider token than any step here has a use for.
    try expect_contains(ci_workflow, "permissions:\n  contents: read\n", ".github/workflows/ci.yml");

    // Unfiltered `push:` plus `pull_request:` doubles up on PR branches; a
    // superseded run should not keep burning a runner. Anchored to the whole
    // line with its `concurrency:` indentation, like the `permissions:` pin
    // above: the bare substring is satisfied by the phrase in a comment, or by
    // `cancel-in-progress: true` nested under something that does not govern
    // this workflow's runs.
    try expect_contains(ci_workflow, "\n  cancel-in-progress: true\n", ".github/workflows/ci.yml");
}

test "the workflow installs fossil and records the version it got" {
    // smoke drives the real `fossil` binary; without it the conformance claim
    // the release rests on is not exercised at all. Anchored to the install
    // command rather than to the bare word "fossil": that word appears in this
    // file's own explanatory comments, so the loose pin stayed green even with
    // the entire install step deleted.
    // The self-hosted runners are developer machines with fossil already
    // installed, so the workflow verifies rather than installs — but it
    // must still HARD-FAIL when fossil is absent, or smoke would be skipped and
    // the conformance claim would quietly stop being exercised. Pinned to the
    // guard, not to the bare word "fossil": that word appears in this file's
    // own comments, so a loose pin stayed green with the whole step deleted.
    try expect_contains(ci_workflow, "command -v fossil >/dev/null 2>&1 || {", ".github/workflows/ci.yml");

    // The release notes have to name the exact version smoke passed against
    // (readiness item S7), which is only knowable if the run prints it — so
    // pin the whole command line, which a prose mention of "fossil version"
    // cannot satisfy.
    try expect_contains(ci_workflow, "\n          fossil version\n", ".github/workflows/ci.yml");

    // Printing alone was not enough (#100). The runners drifted onto different
    // fossils, and because `smoke` is the only lane that runs a real fossil
    // against zit, the conformance claim silently meant a different thing on
    // each: a regression against the newer could pass on the older, and
    // newer-only behaviour reddened the older for something that was not a
    // defect. `fossil sqlite3` quoting its output on 2.26 but not 2.28 cost
    // three CI round trips to find, one of which shipped a fix that fixed
    // nothing. Pinned to the comparison itself rather than to the minimum
    // alone, so deleting the gate and leaving the number behind fails here.
    try expect_contains(ci_workflow, "minimum=\"" ++ pinned_fossil_minimum ++ "\"\n", ".github/workflows/ci.yml");
    try expect_contains(
        ci_workflow,
        "if [ \"$(printf '%s\\n%s\\n' \"$minimum\" \"$version\" | sort -V | head -1)\" != \"$minimum\" ]; then",
        ".github/workflows/ci.yml",
    );
}

test "the workflow runs the whole release battery, in order" {
    // `previous` starts unset rather than at 0 so the first entry is not
    // required to sit at a strictly positive offset — the comparison below is
    // strict, and a battery entry legitimately could begin at byte 0.
    var previous: ?usize = null;
    for (battery) |command| {
        const at = std.mem.indexOf(u8, ci_workflow, command) orelse {
            std.debug.print("\nci pin: .github/workflows/ci.yml has no line \"{s}\"", .{command});
            return error.CiPinMissing;
        };

        // Each battery command runs exactly once. Ordering alone cannot make
        // this claim: duplicating a step leaves every entry's *first* offset
        // unchanged, so the comparison below stays satisfied while the battery
        // silently burns a slow step twice.
        const runs = count_occurrences(ci_workflow, command);
        if (runs != 1) {
            std.debug.print("\nci pin: .github/workflows/ci.yml runs \"{s}\" {d} times; " ++
                "each battery command is run exactly once\n", .{ command, runs });
            return error.CiPinMissing;
        }

        // Ordering is part of the claim: format check before build before
        // tests, so a run fails at the cheapest step that can catch the fault
        // rather than after the slow ones have burned runner minutes.
        // Strict, not `<`: with the exactly-once pin above, equal offsets are
        // unreachable, so the strictness is a statement of intent rather than a
        // live guard. Note the remaining softness — `indexOf` finds the *first*
        // occurrence, so a command named in a comment above its real step is
        // what gets ordered.
        // That is the text-assertion trade this module's header declares.
        if (previous) |before| if (at <= before) {
            std.debug.print("\nci pin: .github/workflows/ci.yml runs \"{s}\" out of order; " ++
                "the battery in src/ci_test.zig is ordered cheapest-failure-first\n", .{command});
            return error.CiPinMissing;
        };
        previous = at;
    }
}

test "the workflow enumerates the complete fuzz registry through list mode" {
    // A failed bare fuzz invocation is summarized by `zig build`; on Linux its
    // leading target names can be truncated before the workflow parses them.
    // Successful list mode makes complete enumeration part of the CLI contract.
    try expect_contains(
        ci_workflow,
        "targets=\"$(zig build fuzz -- --list)\"\n",
        ".github/workflows/ci.yml",
    );
}

test "the workflow is one matrix job gated on explicit native runners" {
    // One matrix job runs the identical battery on every native release target.
    // Its conjunctive labels prevent a leg from falling onto an arbitrary
    // self-hosted runner, while each matrix entry names one dedicated machine.
    try expect_contains(
        ci_workflow,
        "\n    runs-on: [self-hosted, \"${{ matrix.os }}\", \"${{ matrix.arch }}\", \"${{ matrix.runner }}\"]\n",
        ".github/workflows/ci.yml",
    );
    try expect_contains(ci_workflow, "runner: mac-mini-zit\n", ".github/workflows/ci.yml");
    try expect_contains(ci_workflow, "runner: linux-x64-zit\n", ".github/workflows/ci.yml");
    try expect_contains(ci_workflow, "runner: linux-arm64-zit\n", ".github/workflows/ci.yml");

    const jobs = count_occurrences(ci_workflow, "runs-on:");
    if (jobs != 1) {
        std.debug.print("\nci pin: .github/workflows/ci.yml declares {d} jobs (`runs-on:` keys); " ++
            "0.1.1 gates all native targets through exactly one matrix job\n", .{jobs});
        return error.CiPinMissing;
    }
}

test "the release workflow builds every artifact from an explicit baseline target" {
    try expect_contains(
        release_workflow,
        "run: zig build --release=fast -Dtarget=\"${ZIG_TARGET}\"\n",
        ".github/workflows/release.yml",
    );
    try expect_contains(release_workflow, "zig_target: aarch64-macos\n", ".github/workflows/release.yml");
    try expect_contains(release_workflow, "zig_target: x86_64-linux\n", ".github/workflows/release.yml");
    try expect_contains(release_workflow, "zig_target: aarch64-linux\n", ".github/workflows/release.yml");
}

test "the release workflow builds from the exact Fossil release tag" {
    try expect_contains(
        release_workflow,
        "tag=\"version-${version}\"\n",
        ".github/workflows/release.yml",
    );
    try expect_contains(
        release_workflow,
        "fossil open \"${repo}\" \"${tag}\"\n",
        ".github/workflows/release.yml",
    );
}

test "the release workflow qualifies packaged Linux binaries below the build hosts" {
    try expect_contains(
        release_workflow,
        "uses: actions/download-artifact@v4\n",
        ".github/workflows/release.yml",
    );
    try expect_contains(release_workflow, "runs-on: ubuntu-24.04\n", ".github/workflows/release.yml");
    try expect_contains(
        release_workflow,
        "qemu-x86_64 -cpu Nehalem \"${binary}\" version\n",
        ".github/workflows/release.yml",
    );
    try expect_contains(
        release_workflow,
        "qemu-aarch64 -cpu cortex-a53 \"${binary}\" version\n",
        ".github/workflows/release.yml",
    );
}

test "every build.zig step is either run by CI or a reviewed exception" {
    // The negative-space pin. Scans build.zig for `b.step("<name>"` /
    // `env.b.step("<name>"` declarations and requires each name to be
    // accounted for. Text matching, as elsewhere in this file: a step whose
    // name is computed rather than a literal is invisible here, which is
    // stated rather than assumed. build.zig declares them all literally today.
    const marker = ".step(\"";

    var seen = [_]bool{false} ** declared_steps.len;

    var scan: usize = 0;
    while (std.mem.indexOfPos(u8, build_source, scan, marker)) |hit| {
        const start = hit + marker.len;
        const end = std.mem.indexOfScalarPos(u8, build_source, start, '"') orelse {
            // Unreachable while build.zig compiles, but failing loudly beats a
            // `break` that would silently truncate the scan.
            std.debug.print("\nci pin: build.zig has an unterminated .step(\"...) literal " ++
                "at byte offset {d}; the scan cannot continue\n", .{start});
            return error.CiPinMissing;
        };
        scan = end + 1;

        const name = build_source[start..end];

        for (declared_steps, &seen) |expected, *hit_before| {
            if (std.mem.eql(u8, expected, name)) {
                hit_before.* = true;
                break;
            }
        } else {
            std.debug.print("\nci pin: build.zig declares step \"{s}\", which src/ci_test.zig's " ++
                "`declared_steps` does not list. Add it there (and decide whether CI runs it) " ++
                "so the scan keeps describing the real step set.\n", .{name});
            return error.CiPinMissing;
        }

        // The invocation is matched as a whole `run:` line, not as a substring:
        // `zig build e2e` contains `zig build e`, so an unanchored match would
        // let a step named "e" read as covered by the e2e leg.
        var invocation_buf: [128]u8 = undefined;
        const invocation = std.fmt.bufPrint(&invocation_buf, "run: zig build {s}\n", .{name}) catch {
            std.debug.print("\nci pin: build.zig declares a step name longer than {d} bytes " ++
                "(\"{s}\"); widen invocation_buf\n", .{ invocation_buf.len, name });
            return error.CiPinMissing;
        };
        if (std.mem.indexOf(u8, ci_workflow, invocation) != null) continue;

        // Covered, but invoked in a loop rather than as a bare `run:` line.
        // The anchor is verified, not merely trusted: an entry naming a step
        // whose anchor has drifted out of the workflow is a step CI stopped
        // running, and it must fail like any other gap.
        var parameterized_covered = false;
        for (ci_parameterized) |parameterized| {
            if (!std.mem.eql(u8, parameterized.step, name)) continue;
            if (std.mem.indexOf(u8, ci_workflow, parameterized.anchor) == null) {
                std.debug.print("\nci pin: src/ci_test.zig records step \"{s}\" as run by CI " ++
                    "({s}), but .github/workflows/ci.yml no longer contains its invocation. " ++
                    "Either restore the sweep or move the step to `not_in_ci` with a reason.\n", .{ name, parameterized.how });
                return error.CiPinMissing;
            }
            parameterized_covered = true;
            break;
        }
        if (parameterized_covered) continue;

        for (not_in_ci) |exception| {
            if (std.mem.eql(u8, exception.step, name)) break;
        } else {
            std.debug.print("\nci pin: build.zig declares step \"{s}\", which " ++
                ".github/workflows/ci.yml never runs (`zig build {s}`) and src/ci_test.zig's " ++
                "`not_in_ci` list does not excuse. Either add it to the workflow or " ++
                "record why CI skips it.\n", .{ name, name });
            return error.CiPinMissing;
        }
    }

    // The other half of the exact-set assertion, and the vacuity guard: a scan
    // that matched nothing would otherwise pass having checked nothing.
    for (declared_steps, seen) |expected, found| {
        if (found) continue;
        std.debug.print("\nci pin: src/ci_test.zig expects build.zig to declare step \"{s}\", " ++
            "but the scan for `.step(\"...)` did not find it — the step was removed or " ++
            "renamed, or the scan pattern has drifted from the source\n", .{expected});
        return error.CiPinMissing;
    }
}

test "the reviewed not-in-CI exceptions are real build.zig steps that CI does not run" {
    // The other direction: an exception for a step that no longer exists is
    // dead permission. Left unchecked, `not_in_ci` accumulates entries that
    // quietly excuse nothing while looking like they excuse something.
    for (not_in_ci) |exception| {
        var declaration_buf: [128]u8 = undefined;
        const declaration = std.fmt.bufPrint(&declaration_buf, ".step(\"{s}\"", .{exception.step}) catch {
            std.debug.print("\nci pin: not_in_ci names a step longer than {d} bytes " ++
                "(\"{s}\"); widen declaration_buf\n", .{ declaration_buf.len, exception.step });
            return error.CiPinMissing;
        };
        if (std.mem.indexOf(u8, build_source, declaration) == null) {
            std.debug.print("\nci pin: src/ci_test.zig excuses step \"{s}\" from CI (\"{s}\"), " ++
                "but build.zig no longer declares it; drop the stale exception\n", .{ exception.step, exception.why });
            return error.CiPinMissing;
        }

        // And the negative space itself, which is this module's declared
        // purpose and was the one direction nothing asserted: an excused step
        // must actually be absent from the workflow. Without this, adding
        // `zig build bench` to CI would silently falsify that entry's stated
        // reason — "a shared runner's timings are noise, and rule 7 forbids
        // claims not backed by a real measurement" — with no test failing and
        // the excuse left in the file reading as though it still held.
        //
        // Matched as a whole `run:` line for the same reason the forward
        // direction is: `zig build e2e` contains `zig build e`.
        var invocation_buf: [128]u8 = undefined;
        const invocation = std.fmt.bufPrint(&invocation_buf, "run: zig build {s}\n", .{exception.step}) catch {
            std.debug.print("\nci pin: not_in_ci names a step longer than {d} bytes " ++
                "(\"{s}\"); widen invocation_buf\n", .{ invocation_buf.len, exception.step });
            return error.CiPinMissing;
        };
        if (std.mem.indexOf(u8, ci_workflow, invocation) == null) continue;

        std.debug.print("\nci pin: .github/workflows/ci.yml runs \"{s}\", but src/ci_test.zig's " ++
            "`not_in_ci` excuses it from CI (\"{s}\"). Either drop the exception or drop the " ++
            "step from the workflow — the recorded reason no longer describes what CI does.\n", .{ exception.step, exception.why });
        return error.CiPinMissing;
    }
}

test "the shipped release mode is stated, not left to whoever packages" {
    // THIS PIN USED TO REQUIRE ReleaseSafe, on the grounds that src/'s thousand-
    // odd `std.debug.assert` calls are the mechanism by which a detected
    // inconsistency becomes a crash instead of a durable write, and that
    // ReleaseFast compiles every one to nothing. The project has since decided
    // to ship ReleaseFast anyway: `zit pack` runs ~25x faster there, and the
    // write path's answer to the lost assertions is `release_assert_test.zig`,
    // which forbids an assert from standing in for validation of untrusted
    // input in the first place. That guard does not depend on the build mode,
    // which is exactly why it can carry this.
    //
    // What survives the reversal is the reason this was ever a pin rather than
    // a comment: `standardOptimizeOption(.{})` with NO preference leaves the
    // choice to whoever packages, and that failure is silent in both directions
    // — nothing warns at build time, and nothing differs at run time until the
    // behaviour someone assumed turns out not to be the behaviour they got. The
    // hazard is an UNSTATED mode, not a particular one. So this now pins that a
    // preference is stated at all, and that it is the one the project ships.
    //
    // It pins the DEFAULT, not the ceiling: `--release=safe` still selects the
    // checked build for anyone who wants it.
    const preference = ".preferred_optimize_mode = .ReleaseFast";
    if (std.mem.indexOf(u8, build_source, preference) == null) {
        std.debug.print("\nci pin: build.zig no longer states `{s}` in its " ++
            "standardOptimizeOption call. Without a stated preference the release " ++
            "mode is whatever the packager's flags happen to select, and nothing " ++
            "reports the difference until behaviour diverges from what was assumed. " ++
            "Restore it, or if the shipped mode has genuinely changed, update this " ++
            "pin in the same commit so the decision is visible in history.\n", .{preference});
        return error.CiPinMissing;
    }

    // Negative space: the preference is worthless if the call it sits in is not
    // the one that produces the top-level `optimize`. Pin the pairing, not just
    // the substring — a stray mention in a comment must not satisfy this.
    const call = "standardOptimizeOption(.{ .preferred_optimize_mode = .ReleaseFast })";
    if (std.mem.indexOf(u8, build_source, call) == null) {
        std.debug.print("\nci pin: build.zig mentions the ReleaseFast preference but not as " ++
            "`{s}`. The preference only binds when it is passed to the call that yields the " ++
            "top-level optimize mode.\n", .{call});
        return error.CiPinMissing;
    }
}