From f309474be756378337c4f929e8813fe344866adb Mon Sep 17 00:00:00 2001 From: Kyle Sexton <153232337+kyle-sexton@users.noreply.github.com> Date: Fri, 7 Aug 2026 17:48:22 -0400 Subject: [PATCH 1/3] ci: verify the shared hook lib on Windows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `lib/hook-utils.sh` carries OSTYPE-gated branches that a Linux runner never executes: case-insensitive path folding for the Windows filesystem, and `cygpath` short-name (8.3) resolution. Every CI job in this repository runs on ubuntu-24.04, so those branches have only ever been exercised by whatever a maintainer happened to run on a dev box. This adds the job that makes them a gate. Deliberately scoped to one suite. The rest of the corpus is platform-agnostic bash string logic that Linux exercises identically — `lib/powershell/ ps-command.sh` classifies PowerShell commands yet carries zero OSTYPE / cygpath / uname branches, so Linux tests it faithfully. Adding a platform-agnostic suite here would buy no coverage while paying Windows' process-creation cost, measured at ~140ms per spawn against ~3ms on Linux. Free: GitHub Actions is free for public repositories on standard GitHub-hosted runners, Windows included; only larger runners are charged. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/ci.yml | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1fdf1611d0..6e3d734b66 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -256,6 +256,39 @@ jobs: paths: . fail-on-severity: low + # The ONLY job on a Windows runner, and deliberately so. Every other suite in + # this repository is platform-agnostic bash string logic that a Linux runner + # exercises identically — `lib/powershell/ps-command.sh`, for instance, carries + # zero `OSTYPE`/`cygpath`/`uname` branches despite classifying PowerShell + # commands, so Linux tests it faithfully. + # + # `lib/hook-utils.sh` is the exception: it carries OSTYPE-gated branches that a + # Linux runner NEVER EXECUTES — case-insensitive path folding for the Windows + # filesystem, and `cygpath` short-name (8.3) resolution. Those branches were + # previously verified only by whatever a maintainer happened to run on a dev + # box. This job is what makes them a gate. + # + # Free: GitHub Actions is free for public repositories on standard + # GitHub-hosted runners, Windows included; only larger runners are charged. + # https://docs.github.com/en/billing/managing-billing-for-your-products/managing-billing-for-github-actions/about-billing-for-github-actions + # + # Keep this job SMALL. Adding a platform-agnostic suite here buys no coverage + # and pays Windows' process-creation cost (~140ms/spawn vs ~3ms on Linux), + # which is the whole reason the full suite is unusable on a Windows dev box. + hook-utils-windows: + runs-on: windows-latest + timeout-minutes: 20 + defaults: + run: + shell: bash + steps: + - name: Check out + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - name: Run shared lib tests on Windows + run: bash lib/hook-utils.test.sh + hook-utils-sync: runs-on: ubuntu-24.04 timeout-minutes: 15 From 5662d665ade46f44de4a3ae3e97f1b9c99dbcc96 Mon Sep 17 00:00:00 2001 From: Kyle Sexton <153232337+kyle-sexton@users.noreply.github.com> Date: Fri, 7 Aug 2026 17:54:29 -0400 Subject: [PATCH 2/3] ci: pin the Windows runner image instead of windows-latest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The runner policy forbids the floating `*-latest` labels outright (policy.json forbiddenHostedRunnerLabels), for the same reason the Linux lanes pin ubuntu-24.04: an image roll should be a reviewed commit, not a silent Tuesday. Verified locally against both gates — actionlint clean, `runner-policy.mjs --root .` passes. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/ci.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6e3d734b66..d94d7f9386 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -275,8 +275,12 @@ jobs: # Keep this job SMALL. Adding a platform-agnostic suite here buys no coverage # and pays Windows' process-creation cost (~140ms/spawn vs ~3ms on Linux), # which is the whole reason the full suite is unusable on a Windows dev box. + # Pinned, not `windows-latest`: the runner policy forbids the floating + # `*-latest` labels outright (policy.json forbiddenHostedRunnerLabels), for the + # same reason the Linux lanes pin ubuntu-24.04 — an image roll must be a + # reviewed commit, not a silent Tuesday. hook-utils-windows: - runs-on: windows-latest + runs-on: windows-2025 timeout-minutes: 20 defaults: run: From dab65cd78bc3e6b3008a820171815f11a73c6049 Mon Sep 17 00:00:00 2001 From: Kyle Sexton <153232337+kyle-sexton@users.noreply.github.com> Date: Sat, 8 Aug 2026 00:03:11 -0400 Subject: [PATCH 3/3] ci: make the Windows lane block the merge it claimed to gate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hook-utils-windows was absent from the ci-status needs graph. ci-status is the single required check and derives its verdict from join(needs.*.result), so the Windows lane was informational: a Windows-only regression in lib/hook-utils.sh would go red while the required aggregate reported success — the exact hole the job comment claimed to close. Adding it to needs is sufficient, since the aggregate reads the needs graph rather than a hand-maintained list. Verified no job in that graph carries a job-level `if:`, so none can report `skipped` into the `case` arm that treats a non-success result as a failure. The job comment now says where the gating actually comes from, so the next lane added here does not repeat this. runner-policy passed. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/ci.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d94d7f9386..ae6c72e50a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -266,7 +266,11 @@ jobs: # Linux runner NEVER EXECUTES — case-insensitive path folding for the Windows # filesystem, and `cygpath` short-name (8.3) resolution. Those branches were # previously verified only by whatever a maintainer happened to run on a dev - # box. This job is what makes them a gate. + # box. This job is what makes them a gate — but only because it is also listed + # in the `ci-status` needs graph below. `ci-status` is the single required merge + # check, so a lane missing from that list is informational no matter how loudly + # a comment here calls it a gate: the aggregate would report success while this + # lane was red. # # Free: GitHub Actions is free for public repositories on standard # GitHub-hosted runners, Windows included; only larger runners are charged. @@ -997,6 +1001,7 @@ jobs: needs: - hygiene - hook-utils-sync + - hook-utils-windows - parse-concern-value-sync - resolve-convention-pattern-sync - standards-contract-sync