From 1f98bd26e12075a1a748107a52c1b920a81138db Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Tue, 22 Sep 2026 13:31:46 +0100 Subject: [PATCH 1/2] fix(ci): re-pin codeql-action to v4.38.0 SHA Dependabot #977 (68acee77, merged 2026-09-22) bumped codeql-action from 4.38.0 back to 4.38.1 -- the version #973 had just escaped because it fails GitHub's workflow-startup validation estate-wide (nexia-list#100). Measured on hyperpolymath/standards within the hour: codeql.yml red from 12:09Z after four greens, scorecard.yml red from 11:51Z, and the newest run of each reports jobs=0. That is startup death, not a failing job; note GitHub surfaces it here as conclusion=failure, not startup_failure, because the death is in the called reusable. Five refs across three workflows went back to 1c5b675 (v4.38.1): codeql-reusable.yml init, analyze hypatia-scan-reusable.yml upload-sarif scorecard-reusable.yml upload-sarif x2 The first three are the more dangerous shape: #977 replaced the SHA but inherited #973's comment, so each line reads "@1c5b675... # v4.38.0 (4.38.1 blocked estate-wide)" -- an annotation asserting the exact opposite of the value it annotates. A reviewer reading the comment sees the safe version. scorecard-reusable.yml was never swept by #973 at all and kept honest "# v3" / "# v4.38.1" comments. Also widens the dependabot hold, which did not hold. The ignore entry named "github/codeql-action" while the workflows reference the subpath actions, and Dependabot treats github/codeql-action/init as its own dependency name -- #977's own body says "Updates `github/codeql-action/init` from 4.38.0 to 4.38.1". So the ignore matched nothing. The entry is now "github/codeql-action*". Without this the next scheduled run reopens the same PR and re-breaks both workflows. actions.lock is deliberately untouched: it already carried b96794f for all three workflows, so the lockfile was the correct side of the drift and the workflows were the stale side. Regenerating it instead -- the cure the gate's own error text prescribes -- would have written 1c5b675 back into the lock and re-legitimised the blocked version. Verified: zero refs to 1c5b675 remain under .github/; the actions-lock gate reports no error-severity and no stale findings (93 pre-existing sha-as-ref warnings are unchanged); git diff on actions.lock is empty. Refs: #973, #977, nexia-list#100, nexia-list#101, nexia-list#104 Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01WPSJ7fBhVAMcpSffCBWUDo Signed-off-by: Jonathan D.A. Jewell <6759885+hyperpolymath@users.noreply.github.com> --- .github/dependabot.yml | 9 ++++++++- .github/workflows/codeql-reusable.yml | 4 ++-- .github/workflows/hypatia-scan-reusable.yml | 2 +- .github/workflows/scorecard-reusable.yml | 4 ++-- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index a91454d4b..db0dd83cd 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -16,7 +16,14 @@ updates: # SHA-form re-bump attempt bypassed versions-scoped ignores — see # nexia-list#101/#104). Hold until upstream clears 4.38.1 or a newer # release verifies green; revisit deliberately, not weekly. - - dependency-name: "github/codeql-action" + # + # The trailing * is load-bearing. Workflows reference the SUBPATH + # actions (github/codeql-action/init, /analyze, /upload-sarif) and + # Dependabot treats each subpath as its own dependency name -- so a + # bare "github/codeql-action" entry matches NONE of them. That is how + # #977 re-bumped 4.38.0 -> 4.38.1 on 2026-09-22 straight through this + # hold, startup-killing codeql.yml and scorecard.yml (jobs=0). + - dependency-name: "github/codeql-action*" # github-actions major bumps are usually safe — the SHA pin is the real # version. Standards repo is the canonical-template host so we want the # PRs fast (daily) and grouped. If a specific action proves unstable on diff --git a/.github/workflows/codeql-reusable.yml b/.github/workflows/codeql-reusable.yml index 9f0158d41..4e13fa00d 100644 --- a/.github/workflows/codeql-reusable.yml +++ b/.github/workflows/codeql-reusable.yml @@ -94,12 +94,12 @@ jobs: persist-credentials: false - name: Initialize CodeQL - uses: github/codeql-action/init@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4.38.0 (4.38.1 blocked estate-wide; nexia-list#100) + uses: github/codeql-action/init@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0 (4.38.1 blocked estate-wide; nexia-list#100) with: languages: ${{ inputs.language }} build-mode: ${{ inputs.build-mode }} - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4.38.0 (4.38.1 blocked estate-wide; nexia-list#100) + uses: github/codeql-action/analyze@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0 (4.38.1 blocked estate-wide; nexia-list#100) with: category: "/language:${{ inputs.language }}" diff --git a/.github/workflows/hypatia-scan-reusable.yml b/.github/workflows/hypatia-scan-reusable.yml index 313f8679d..e4dc53f84 100644 --- a/.github/workflows/hypatia-scan-reusable.yml +++ b/.github/workflows/hypatia-scan-reusable.yml @@ -242,7 +242,7 @@ jobs: # This flag only tolerates a genuine upload failure (e.g. Advanced # Security disabled on a private repo) once the job is actually running. continue-on-error: true - uses: github/codeql-action/upload-sarif@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4.38.0 (4.38.1 blocked estate-wide; nexia-list#100) + uses: github/codeql-action/upload-sarif@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0 (4.38.1 blocked estate-wide; nexia-list#100) with: sarif_file: hypatia.sarif category: hypatia diff --git a/.github/workflows/scorecard-reusable.yml b/.github/workflows/scorecard-reusable.yml index 1f9da93f6..7fa7240c5 100644 --- a/.github/workflows/scorecard-reusable.yml +++ b/.github/workflows/scorecard-reusable.yml @@ -92,7 +92,7 @@ jobs: printf 'reconciled=false\n' >> "$GITHUB_OUTPUT" fi - name: Upload SARIF to code scanning - uses: github/codeql-action/upload-sarif@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v3 + uses: github/codeql-action/upload-sarif@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0 (4.38.1 blocked estate-wide; nexia-list#100) with: sarif_file: ${{ steps.select-sarif.outputs.file }} @@ -225,7 +225,7 @@ jobs: printf 'reconciled=false\n' >> "$GITHUB_OUTPUT" fi - name: Upload SARIF to code scanning - uses: github/codeql-action/upload-sarif@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4.38.1 + uses: github/codeql-action/upload-sarif@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0 (4.38.1 blocked estate-wide; nexia-list#100) with: sarif_file: ${{ steps.select-sarif.outputs.file }} - name: Retain scan evidence From 9dfdb66d90cff3c0615519278cf13d4fa3f0fb3b Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Tue, 22 Sep 2026 13:33:03 +0100 Subject: [PATCH 2/2] fix(gate): accept .github/ and docs/ CONTRIBUTING check-docs-presence.sh searched only the repository root for CONTRIBUTING, while estate repos have been deliberately relocating the file to .github/ -- the location GitHub itself auto-discovers (launch-scaffolder d426ea4d: "the estate canonical location is .github/CONTRIBUTING.md, which GitHub auto-discovers"). The gate and its consumers were therefore asking different questions, and the gate lost. Census of 516 local clones on 2026-09-22: 401 repos carry CONTRIBUTING at the root, 96 under .github/, 1 under docs/, and 94 have none anywhere. So 19 unique repos were being reported missing a document they demonstrably have, against 94 genuine misses. This widens WHERE the gate looks without widening WHAT it asks: the 94 still block. Also corrects the failure message, which named only the root locations and so prescribed a cure narrower than the code accepts (cf. standards#930). Tests: four new accept cases, one per added path, plus an anti-overreach case proving a CONTRIBUTING at an undiscoverable depth still BLOCKS -- that one guards against a future 'fix' by recursive find, which would silently pass all 94. Suite 29/29. Mutants killed, both leaving the pre-existing cases green: gate fully reverted -> the 4 new accept cases fail only .github/CONTRIBUTING.md added -> 3 fail (each path load-bearing) Real-world controls: launch-scaffolder rc=0 (was the reported miss), standards itself rc=0 via 3-practice/, cicd-suite rc=1 (genuine miss). Refs: hyperpolymath/launch-scaffolder#37 Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01WPSJ7fBhVAMcpSffCBWUDo Signed-off-by: Jonathan D.A. Jewell <6759885+hyperpolymath@users.noreply.github.com> --- scripts/check-docs-presence.sh | 31 +++++++++++++++++--- scripts/tests/governance-gates-505-test.sh | 34 ++++++++++++++++++++++ 2 files changed, 61 insertions(+), 4 deletions(-) diff --git a/scripts/check-docs-presence.sh b/scripts/check-docs-presence.sh index fcdc83758..a386759ac 100755 --- a/scripts/check-docs-presence.sh +++ b/scripts/check-docs-presence.sh @@ -24,7 +24,22 @@ # default; GitHub-required community-health files stay Markdown): # README.adoc | README.md # LICENSE | LICENSE.txt | LICENSE.md -# CONTRIBUTING.md | CONTRIBUTING.adoc | 3-practice/CONTRIBUTING.adoc +# CONTRIBUTING.md | CONTRIBUTING.adoc +# | .github/CONTRIBUTING.md | .github/CONTRIBUTING.adoc +# | docs/CONTRIBUTING.md | docs/CONTRIBUTING.adoc +# | 3-practice/CONTRIBUTING.adoc +# +# .github/ and docs/ are accepted because GitHub itself auto-discovers a +# community-health file in either location, and repos in this estate have been +# deliberately relocating theirs there (launch-scaffolder d426ea4d: "the estate +# canonical location is .github/CONTRIBUTING.md, which GitHub auto-discovers"). +# Before this change the gate asked "is there a CONTRIBUTING at the repo root?" +# while its consumers had been told to answer "is there a CONTRIBUTING GitHub +# can find?" -- a guard asking a different question than its consumer. A census +# of 516 local clones on 2026-09-22 found 19 repos reported missing that in fact +# carry the file under .github/ or docs/; 94 are genuinely missing it and are +# unaffected by this change. This strictly widens WHERE the gate looks; it does +# not weaken WHAT it asks. # # CONTRIBUTING.adoc at the repo root is accepted because the estate policy named # above makes AsciiDoc the default, and it is what the estate actually uses: @@ -75,7 +90,7 @@ if [ ! -d "$ROOT" ]; then exit 1 fi -# have ... -> 0 if any of the candidate filenames exists at the root. +# have ... -> 0 if any of the candidate paths exists, relative to the root. have() { local f for f in "$@"; do @@ -90,7 +105,10 @@ grace_missing="" have README.adoc README.md || blocking_missing="$blocking_missing README" have LICENSE LICENSE.txt LICENSE.md || blocking_missing="$blocking_missing LICENSE" -if ! have CONTRIBUTING.md CONTRIBUTING.adoc 3-practice/CONTRIBUTING.adoc; then +if ! have CONTRIBUTING.md CONTRIBUTING.adoc \ + .github/CONTRIBUTING.md .github/CONTRIBUTING.adoc \ + docs/CONTRIBUTING.md docs/CONTRIBUTING.adoc \ + 3-practice/CONTRIBUTING.adoc; then # String comparison is sound here: YYYY-MM-DD sorts chronologically, and both # operands are format-validated above. if [[ "$TODAY" < "$ENFORCE_CONTRIBUTING_FROM" ]]; then @@ -111,7 +129,12 @@ if [ -n "$blocking_missing" ]; then echo "Required at the repository root (either extension where two are listed):" echo " README.adoc (or README.md)" echo " LICENSE (or LICENSE.txt / LICENSE.md)" - echo " CONTRIBUTING.md (or 3-practice/CONTRIBUTING.adoc)" + echo + echo "CONTRIBUTING is accepted at any location GitHub auto-discovers:" + echo " CONTRIBUTING.md (or CONTRIBUTING.adoc)" + echo " .github/CONTRIBUTING.md (or .github/CONTRIBUTING.adoc)" + echo " docs/CONTRIBUTING.md (or docs/CONTRIBUTING.adoc)" + echo " 3-practice/CONTRIBUTING.adoc" echo echo "Estate policy: docs are AsciiDoc by default; see hyperpolymath/standards." exit 1 diff --git a/scripts/tests/governance-gates-505-test.sh b/scripts/tests/governance-gates-505-test.sh index e719abf0c..f154fdc66 100755 --- a/scripts/tests/governance-gates-505-test.sh +++ b/scripts/tests/governance-gates-505-test.sh @@ -83,6 +83,40 @@ assert "root CONTRIBUTING.adoc still warns pre-cutoff only when ABSENT" 0 \ "NOT YET ENFORCED" \ env DOCS_TODAY="$BEFORE" "$DOCS" "$(mkrepo docs-adoc-root-absent README.adoc LICENSE)" +# Regression: GitHub auto-discovers a community-health file under .github/ or +# docs/, and estate repos have been deliberately relocating theirs there +# (launch-scaffolder d426ea4d). The gate looked only at the repo root, so it +# reported those repos "missing" a file that is present and discoverable — a +# guard asking a different question than its consumer. A 516-clone census on +# 2026-09-22 found 19 such repos. Each of the four new paths gets its own case: +# a single .github/CONTRIBUTING.md case would pass even if only that one path +# had been added to the candidate list. +r=$(mkrepo docs-github-md README.adoc LICENSE .github/CONTRIBUTING.md) +assert ".github/CONTRIBUTING.md accepted (regression: launch-scaffolder#37)" 0 \ + "✅ Core documentation present" \ + env DOCS_TODAY="$AFTER" "$DOCS" "$r" + +r=$(mkrepo docs-github-adoc README.adoc LICENSE .github/CONTRIBUTING.adoc) +assert ".github/CONTRIBUTING.adoc accepted" 0 "✅ Core documentation present" \ + env DOCS_TODAY="$AFTER" "$DOCS" "$r" + +r=$(mkrepo docs-docsdir-md README.adoc LICENSE docs/CONTRIBUTING.md) +assert "docs/CONTRIBUTING.md accepted" 0 "✅ Core documentation present" \ + env DOCS_TODAY="$AFTER" "$DOCS" "$r" + +r=$(mkrepo docs-docsdir-adoc README.adoc LICENSE docs/CONTRIBUTING.adoc) +assert "docs/CONTRIBUTING.adoc accepted" 0 "✅ Core documentation present" \ + env DOCS_TODAY="$AFTER" "$DOCS" "$r" + +# Anti-overreach: widening WHERE the gate looks must not widen WHAT it asks. +# A CONTRIBUTING at an arbitrary depth is NOT discoverable by GitHub and must +# still block. Without this case the four above could be "satisfied" by a +# recursive find, which would silently pass the 94 genuinely-missing repos. +r=$(mkrepo docs-deep-nested README.adoc LICENSE src/internal/CONTRIBUTING.md) +assert "CONTRIBUTING at an undiscoverable path still BLOCKS" 1 \ + "Missing required documentation: CONTRIBUTING" \ + env DOCS_TODAY="$AFTER" "$DOCS" "$r" + # README/LICENSE are BLOCKING NOW — the grace window must not shelter them. r=$(mkrepo docs-no-readme LICENSE CONTRIBUTING.md) assert "missing README fails even pre-cutoff" 1 "Missing required documentation: README" \