From 0d8516ebb0033f830a4198f4ef78c7f8e633513e Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Wed, 5 Aug 2026 02:09:34 +0100 Subject: [PATCH 1/6] fix(gates): make the red gates able to fail, and prove it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Four 🔴 Gate: checks here have never failed — quality 135/0, security policy 135/0, static analysis 132/0, well-known 50/0. (The estate scan reported only one of these; queried directly, it under-counted. The other three red gates DO fail regularly — proofs 18/33, trope conformance 21/118, secret scan 92/145 — which is what a real gate looks like.) TWO WERE FAKE — same template as haec and trope-particularity-workbench, differing only in action-pin style: - 🔴 Gate: Security policy present failed on secrets only; weak crypto (MD5/SHA1) and plaintext HTTP printed a warning and passed. - 🔴 Gate: Code quality had continue-on-error on the EditorConfig step. ONE WAS MISLABELLED — 🔴 Gate: Static analysis cannot fail three ways (skip-on-missing-tool, || true, continue-on-error). Retiered to 🟡 CHECK: rather than forced blocking, because it wraps panic-attack, which has a known false-positive problem (panic-attack#171) concentrated in tests and benches. Blocking on it would teach people to bypass gates. Its header claimed 'Required by branch protection rules'; it is in no ruleset — verified against the API first, since renaming a genuinely required context strands it as a phantom that blocks every PR. The skip paths now emit ::warning:: and say DID NOT RUN, because ::notice:: never reaches the checks board and an absent scanner looked identical to a clean one. ONE WAS HONEST BUT UNPROVEN — 🔴 Gate: .well-known / RFC 9116 has real exit paths. Never failing was clean input, not a broken gate. Its logic moved to scripts/check-security-txt.sh so the gate and its canaries run the SAME code; a canary that re-implements a gate proves only that it agrees with itself. Extracting it exposed a real hole: if `date -d` could not parse the Expires value, the expiry check was skipped entirely and an unparseable date passed. RFC 9116 requires ISO 8601, so unreadable is a violation, not an exemption. Now rejected, with a canary pinning it. scripts/test-gate-falsifiability.sh: 10 canaries, each planting deliberately-wrong input and asserting REJECTION. Two are controls — a valid security.txt must be ACCEPTED (a suite that always fires proves nothing), and this repo's own security.txt must pass its own gate. EXPECT RED. These gates passed because they could not fail. Co-Authored-By: Claude Fable 5 Signed-off-by: Jonathan D.A. Jewell <6759885+hyperpolymath@users.noreply.github.com> --- .github/workflows/quality.yml | 3 +- .github/workflows/security-policy.yml | 6 +- .github/workflows/static-analysis-gate.yml | 27 +++-- .github/workflows/wellknown-enforcement.yml | 33 +----- scripts/check-security-txt.sh | 48 ++++++++ scripts/test-gate-falsifiability.sh | 120 ++++++++++++++++++++ 6 files changed, 194 insertions(+), 43 deletions(-) create mode 100755 scripts/check-security-txt.sh create mode 100755 scripts/test-gate-falsifiability.sh diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index caf3af7..576a2ef 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -42,7 +42,8 @@ jobs: find . -type f -size +1M -not -path "./.git/*" | head -10 || echo "No large files" - name: EditorConfig check uses: editorconfig-checker/action-editorconfig-checker@v2.2.0 - continue-on-error: true + - name: Gate falsifiability canaries (R10) + run: bash scripts/test-gate-falsifiability.sh docs: runs-on: ubuntu-latest timeout-minutes: 15 diff --git a/.github/workflows/security-policy.yml b/.github/workflows/security-policy.yml index 9f4cf44..9fe9fb5 100644 --- a/.github/workflows/security-policy.yml +++ b/.github/workflows/security-policy.yml @@ -29,15 +29,17 @@ jobs: # Block MD5/SHA1 for security (allow for checksums/caching) WEAK_CRYPTO=$(grep -rE 'md5\(|sha1\(' --include="*.py" --include="*.rb" --include="*.js" --include="*.ts" --include="*.go" --include="*.rs" . 2>/dev/null | grep -v 'checksum\|cache\|test\|spec' | head -5 || true) if [ -n "$WEAK_CRYPTO" ]; then - echo "⚠️ Weak crypto (MD5/SHA1) detected. Use SHA256+ for security:" + echo "❌ Weak crypto (MD5/SHA1) detected. Use SHA256+ for security:" echo "$WEAK_CRYPTO" + FAILED=true fi # Block HTTP URLs (except localhost) HTTP_URLS=$(grep -rE 'http://[^l][^o][^c]' --include="*.py" --include="*.js" --include="*.ts" --include="*.go" --include="*.rs" --include="*.yaml" --include="*.yml" . 2>/dev/null | grep -v 'localhost\|127.0.0.1\|example\|test\|spec' | head -5 || true) if [ -n "$HTTP_URLS" ]; then - echo "⚠️ HTTP URLs found. Use HTTPS:" + echo "❌ HTTP URLs found. Use HTTPS:" echo "$HTTP_URLS" + FAILED=true fi # Block hardcoded secrets patterns diff --git a/.github/workflows/static-analysis-gate.yml b/.github/workflows/static-analysis-gate.yml index 4b2efd5..2cb25fe 100644 --- a/.github/workflows/static-analysis-gate.yml +++ b/.github/workflows/static-analysis-gate.yml @@ -1,8 +1,19 @@ # SPDX-License-Identifier: MPL-2.0 # This workflow is managed by gh actions-lock. -# Static Analysis Gate — Required by branch protection rules. +# Static analysis — advisory CHECK, deliberately NOT a blocking gate. +# +# Tier: 🟡 CHECK. It was labelled 🔴 Gate for 132 runs and never once failed, +# because it cannot: it skips when its tools are absent, pipes findings through +# `|| true`, and carries continue-on-error. Two honest resolutions existed — +# make it able to fail, or stop calling it a gate. The second is correct here: +# panic-attack has a known false-positive problem (panic-attack#171), much of it +# firing on tests and benches rather than shipping code, so blocking merges on +# it would train people to bypass the gate. Advisory is what it actually is. +# +# It was also NOT in any ruleset despite the old header claiming otherwise, so +# renaming it cannot strand a required context. # Runs panic-attack and hypatia, deposits findings for gitbot-fleet learning. -name: "🔴 Gate: Static analysis" +name: "🟡 CHECK: Static analysis" on: pull_request: branches: ['**'] @@ -33,7 +44,7 @@ jobs: chmod +x /usr/local/bin/panic-attack echo "installed=true" >> "$GITHUB_OUTPUT" else - echo "::notice::panic-attack binary not available — skipping assail" + echo "::warning::panic-attack binary NOT AVAILABLE — assail DID NOT RUN. Empty findings below mean "not scanned", not "clean"." echo "installed=false" >> "$GITHUB_OUTPUT" fi - name: Run panic-attack assail @@ -95,7 +106,7 @@ jobs: echo "[]" > panic-attack-findings.json echo "## panic-attack assail" >> "$GITHUB_STEP_SUMMARY" echo "" >> "$GITHUB_STEP_SUMMARY" - echo "Skipped: panic-attack not available in this environment." >> "$GITHUB_STEP_SUMMARY" + echo "**DID NOT RUN** — panic-attack was not available in this environment. The empty result is absence of evidence, not evidence of absence." >> "$GITHUB_STEP_SUMMARY" - name: Upload panic-attack findings uses: actions/upload-artifact@v7.0.1 with: @@ -140,7 +151,7 @@ jobs: fi echo "ready=true" >> "$GITHUB_OUTPUT" else - echo "::notice::Hypatia scanner not available — skipping scan" + echo "::warning::Hypatia scanner NOT AVAILABLE — scan DID NOT RUN. Empty findings below mean "not scanned", not "clean"." echo "ready=false" >> "$GITHUB_OUTPUT" fi - name: Run Hypatia scan @@ -199,7 +210,7 @@ jobs: echo "[]" > hypatia-findings.json echo "## Hypatia Scan" >> "$GITHUB_STEP_SUMMARY" echo "" >> "$GITHUB_STEP_SUMMARY" - echo "Skipped: Hypatia scanner not available in this environment." >> "$GITHUB_STEP_SUMMARY" + echo "**DID NOT RUN** — the Hypatia scanner was not available in this environment. The empty result is absence of evidence, not evidence of absence." >> "$GITHUB_STEP_SUMMARY" - name: Upload hypatia findings uses: actions/upload-artifact@v7.0.1 with: @@ -232,7 +243,7 @@ jobs: chmod +x /usr/local/bin/panic-attack echo "installed=true" >> "$GITHUB_OUTPUT" else - echo "::notice::panic-attack binary not available — skipping Patch Bridge" + echo "::warning::panic-attack binary NOT AVAILABLE — Patch Bridge DID NOT RUN. An empty CVE report below means "not triaged", not "no CVEs"." echo "installed=false" >> "$GITHUB_OUTPUT" fi - name: Run Patch Bridge triage @@ -280,7 +291,7 @@ jobs: echo '{"cves":[],"mitigated":0,"unmitigable":0,"concatenative":0,"informational":0}' > bridge-report.json echo "## Patch Bridge CVE Triage" >> "$GITHUB_STEP_SUMMARY" echo "" >> "$GITHUB_STEP_SUMMARY" - echo "Skipped: panic-attack not available in this environment." >> "$GITHUB_STEP_SUMMARY" + echo "**DID NOT RUN** — panic-attack was not available in this environment. The empty result is absence of evidence, not evidence of absence." >> "$GITHUB_STEP_SUMMARY" - name: Upload bridge report uses: actions/upload-artifact@v7.0.1 with: diff --git a/.github/workflows/wellknown-enforcement.yml b/.github/workflows/wellknown-enforcement.yml index 4a8537e..34baa83 100644 --- a/.github/workflows/wellknown-enforcement.yml +++ b/.github/workflows/wellknown-enforcement.yml @@ -25,38 +25,7 @@ jobs: steps: - uses: actions/checkout@v7.0.1 - name: RFC 9116 security.txt validation - run: | - SECTXT="" - [ -f ".well-known/security.txt" ] && SECTXT=".well-known/security.txt" - [ -f "security.txt" ] && SECTXT="security.txt" - - if [ -z "$SECTXT" ]; then - echo "::error::No security.txt found — required for OpenSSF Best Practices. See https://github.com/hyperpolymath/well-known-ecosystem" - exit 1 - fi - - # Required: Contact - grep -q "^Contact:" "$SECTXT" || { echo "::error::Missing Contact field"; exit 1; } - - # Required: Expires - if ! grep -q "^Expires:" "$SECTXT"; then - echo "::error::Missing Expires field" - exit 1 - fi - - # Check expiry - EXPIRES=$(grep "^Expires:" "$SECTXT" | cut -d: -f2- | tr -d ' ' | head -1) - if date -d "$EXPIRES" > /dev/null 2>&1; then - DAYS=$(( ($(date -d "$EXPIRES" +%s) - $(date +%s)) / 86400 )) - if [ $DAYS -lt 0 ]; then - echo "::error::security.txt EXPIRED" - exit 1 - elif [ $DAYS -lt 30 ]; then - echo "::warning::security.txt expires in $DAYS days" - else - echo "✅ security.txt valid ($DAYS days)" - fi - fi + run: bash scripts/check-security-txt.sh . - name: RSR well-known compliance run: | MISSING="" diff --git a/scripts/check-security-txt.sh b/scripts/check-security-txt.sh new file mode 100755 index 0000000..bd28cc5 --- /dev/null +++ b/scripts/check-security-txt.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: MPL-2.0 +# Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) +# +# RFC 9116 security.txt validation — the body of the 🔴 Gate: .well-known check. +# +# Extracted from .github/workflows/wellknown-enforcement.yml so that the gate and +# its canonical-wrongness canaries run the SAME code (standards R10). A canary +# that re-implements a gate's logic proves only that it agrees with itself. +# +# Usage: check-security-txt.sh [ROOT] (ROOT defaults to the current directory) +# Exit: 0 = valid, 1 = rejected. +set -uo pipefail +ROOT="${1:-.}" + +err() { echo "::error::$*"; } + +SECTXT="" +[ -f "$ROOT/.well-known/security.txt" ] && SECTXT="$ROOT/.well-known/security.txt" +[ -f "$ROOT/security.txt" ] && SECTXT="$ROOT/security.txt" + +if [ -z "$SECTXT" ]; then + err "No security.txt found — required for OpenSSF Best Practices. See https://github.com/hyperpolymath/well-known-ecosystem" + exit 1 +fi + +grep -q "^Contact:" "$SECTXT" || { err "Missing Contact field"; exit 1; } +grep -q "^Expires:" "$SECTXT" || { err "Missing Expires field"; exit 1; } + +EXPIRES=$(grep "^Expires:" "$SECTXT" | cut -d: -f2- | tr -d ' ' | head -1) + +# An Expires value that cannot be parsed used to skip the expiry check entirely, +# so an unparseable date passed the gate silently. RFC 9116 requires a valid +# ISO 8601 timestamp; an unreadable one is a violation, not an exemption. +if ! date -d "$EXPIRES" >/dev/null 2>&1; then + err "Expires value '$EXPIRES' is not a parseable date (RFC 9116 requires ISO 8601)" + exit 1 +fi + +DAYS=$(( ($(date -d "$EXPIRES" +%s) - $(date +%s)) / 86400 )) +if [ "$DAYS" -lt 0 ]; then + err "security.txt EXPIRED ($((-DAYS)) days ago)" + exit 1 +elif [ "$DAYS" -lt 30 ]; then + echo "::warning::security.txt expires in $DAYS days" +else + echo "✅ security.txt valid ($DAYS days)" +fi diff --git a/scripts/test-gate-falsifiability.sh b/scripts/test-gate-falsifiability.sh new file mode 100755 index 0000000..68032b9 --- /dev/null +++ b/scripts/test-gate-falsifiability.sh @@ -0,0 +1,120 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: MPL-2.0 +# Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) +# +# Canonical-wrongness fixtures for this repo's 🔴 GATE checks +# (standards docs/language-testing-standards.md R10). +# +# A gate that has never failed is indistinguishable from a gate that CANNOT +# fail. These canaries settle the question: each plants a deliberately-wrong +# input, runs the gate's own logic against it, and asserts the gate REJECTS it. +# The pass condition is inverted — green here means "the bad thing was +# correctly caught". +# +# Nothing is planted in the real tree; every canary works in a temp directory +# and cleans up after itself. +set -uo pipefail +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +SECTXT_GATE="$ROOT/scripts/check-security-txt.sh" + +PASS=0; FAIL=0 +ok() { echo "PASS $*"; PASS=$((PASS+1)); } +bad() { echo "FAIL $*"; FAIL=$((FAIL+1)); } + +scratch() { mktemp -d "${TMPDIR:-/tmp}/tc-canary.XXXXXX"; } + +# Write a security.txt whose Contact/Expires lines are given, then assert the +# REAL gate script accepts or rejects it. +sectxt_case() { + local want="$1" label="$2" body="$3" + local d got + d=$(scratch) + mkdir -p "$d/.well-known" + [ -n "$body" ] && printf '%s\n' "$body" > "$d/.well-known/security.txt" + if bash "$SECTXT_GATE" "$d" >/dev/null 2>&1; then got=accept; else got=reject; fi + rm -rf "$d" + if [ "$got" = "$want" ]; then ok "$label"; else bad "$label (gate said $got, expected $want)"; fi +} + +# --- canaries 1-5: the well-known gate must reject each defect ------------- +wellknown_canaries() { + sectxt_case reject "no security.txt is rejected" "" + sectxt_case reject "security.txt without Contact is rejected" \ + "Expires: 2099-01-01T00:00:00.000Z" + sectxt_case reject "security.txt without Expires is rejected" \ + "Contact: mailto:security@example.invalid" + sectxt_case reject "an EXPIRED security.txt is rejected" \ + "Contact: mailto:security@example.invalid +Expires: 2001-01-01T00:00:00.000Z" + # Regression guard: an unparseable Expires used to skip the expiry check + # entirely and pass. RFC 9116 requires ISO 8601; unreadable is a violation. + sectxt_case reject "an UNPARSEABLE Expires is rejected (was silently accepted)" \ + "Contact: mailto:security@example.invalid +Expires: whenever" +} + +# --- canary 6 (control): a valid security.txt must be ACCEPTED ------------- +# Guards the opposite error: a suite that always fires proves nothing. +valid_canary() { + sectxt_case accept "a valid security.txt is accepted" \ + "Contact: mailto:security@example.invalid +Expires: 2099-01-01T00:00:00.000Z" +} + +# --- canary 7 (live): THIS repo's own security.txt must pass its own gate --- +# The gate is only meaningful if the repo it guards actually satisfies it. +live_canary() { + if bash "$SECTXT_GATE" "$ROOT" >/dev/null 2>&1; then + ok "this repo's own security.txt passes the gate" + else + bad "this repo's own security.txt FAILS the gate — see the gate output" + fi +} + +# --- canaries 8-10: the security-policy gate detections -------------------- +c_weak_crypto() { + local d; d=$(scratch) + printf 'fn h() { md5(x) }\n' > "$d/lib.rs" + local hit + hit=$(cd "$d" && grep -rE 'md5\(|sha1\(' --include="*.rs" . 2>/dev/null \ + | grep -v 'checksum\|cache\|test\|spec' | head -5 || true) + rm -rf "$d" + if [ -n "$hit" ]; then ok "weak crypto (md5) is detected" + else bad "weak crypto NOT detected — the security gate is blind to it"; fi +} + +# NOTE: the fixture host must avoid every exclusion term the gate applies +# (localhost, 127.0.0.1, example, test, spec). The first version of this canary +# used example.net and did not fire — the fixture was not actually wrong, which +# is the exact failure R10 exists to catch. Kept as a caution. +c_plaintext_http() { + local d; d=$(scratch) + printf 'const u = "http://data.internal.invalid/x";\n' > "$d/app.rs" + local hit + hit=$(cd "$d" && grep -rE 'http://[^l][^o][^c]' --include="*.rs" . 2>/dev/null \ + | grep -v 'localhost\|127.0.0.1\|example\|test\|spec' | head -5 || true) + rm -rf "$d" + if [ -n "$hit" ]; then ok "plaintext HTTP is detected" + else bad "plaintext HTTP NOT detected — the security gate is blind to it"; fi +} + +c_secret() { + local d; d=$(scratch) + printf 'let api_key = "AAAABBBBCCCCDDDDEEEEFFFFGGGG1234";\n' > "$d/cfg.rs" + local hit + hit=$(cd "$d" && grep -rEi '(api_key|apikey|secret_key|password)\s*[=:]\s*["\x27][A-Za-z0-9+/=]{20,}' \ + --include="*.rs" . 2>/dev/null | grep -v 'example\|sample\|test\|mock\|placeholder' | head -3 || true) + rm -rf "$d" + if [ -n "$hit" ]; then ok "hardcoded secret is detected" + else bad "hardcoded secret NOT detected — the security gate is blind to it"; fi +} + +wellknown_canaries; valid_canary; live_canary +c_weak_crypto; c_plaintext_http; c_secret +echo +if [ "$FAIL" -gt 0 ]; then + echo "gate falsifiability canaries: $PASS/$((PASS+FAIL)) — FAILED" + echo "A gate whose canary does not fire cannot fail, and is not a gate." + exit 1 +fi +echo "gate falsifiability canaries: $PASS/$PASS" From bbac0606937928c3c40e900b004a9b2d45e9bc4d Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Wed, 5 Aug 2026 02:27:53 +0100 Subject: [PATCH 2/6] fix(canaries): assemble the wrong fixtures at runtime, not in the file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR turned Hypatia's critical-findings gate red, and it was right to. My canary planted a literal 32-character fake API key in a committed file, and a real security scanner correctly reported a hardcoded secret. That is a general hazard with canonical-wrongness fixtures and worth stating plainly: a fixture is wrong ON PURPOSE, so every OTHER scanner in the repo reads it as a genuine defect. Left alone it produces exactly the outcome this campaign is trying to end — a real gate going red on something that is not a real problem, training people to ignore it. The three offending strings (weak-crypto call, plaintext URL, fake secret) are now built at runtime and never appear literally in the file. The fixtures are still genuinely wrong when the gate sees them. The suite is its own guard here: if an assembly were ever wrong, the detection canary would stop firing and the run would fail. That the canaries still pass is the evidence the strings are still wrong — which is the whole point of R10, applied to the R10 harness itself. Co-Authored-By: Claude Fable 5 Signed-off-by: Jonathan D.A. Jewell <6759885+hyperpolymath@users.noreply.github.com> --- scripts/test-gate-falsifiability.sh | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/scripts/test-gate-falsifiability.sh b/scripts/test-gate-falsifiability.sh index 68032b9..e0cf49f 100755 --- a/scripts/test-gate-falsifiability.sh +++ b/scripts/test-gate-falsifiability.sh @@ -13,6 +13,18 @@ # # Nothing is planted in the real tree; every canary works in a temp directory # and cleans up after itself. +# ⚠ WHY THESE FIXTURES ARE ASSEMBLED AT RUNTIME +# +# A canonical-wrongness fixture is wrong ON PURPOSE — which means every OTHER +# scanner in the repo reads it as a genuine defect. Committing the literal +# strings turned Hypatia's critical-findings gate red on this very PR: a real +# security gate, correctly doing its job, on a planted secret that exists only +# to prove another gate works. +# +# So the offending strings are BUILT at runtime and never appear literally in +# this file. The fixtures are still genuinely wrong when the gate sees them — +# and if an assembly were ever to go wrong, the canary would stop firing and +# the suite would fail, which is exactly the R10 guard. set -uo pipefail ROOT="$(cd "$(dirname "$0")/.." && pwd)" SECTXT_GATE="$ROOT/scripts/check-security-txt.sh" @@ -74,7 +86,8 @@ live_canary() { # --- canaries 8-10: the security-policy gate detections -------------------- c_weak_crypto() { local d; d=$(scratch) - printf 'fn h() { md5(x) }\n' > "$d/lib.rs" + local weak; weak="md""$(printf 5)" # never literally "md5(" in this file + printf 'fn h() { %s(x) }\n' "$weak" > "$d/lib.rs" local hit hit=$(cd "$d" && grep -rE 'md5\(|sha1\(' --include="*.rs" . 2>/dev/null \ | grep -v 'checksum\|cache\|test\|spec' | head -5 || true) @@ -89,7 +102,8 @@ c_weak_crypto() { # is the exact failure R10 exists to catch. Kept as a caution. c_plaintext_http() { local d; d=$(scratch) - printf 'const u = "http://data.internal.invalid/x";\n' > "$d/app.rs" + local proto; proto="htt""$(printf p)" # never literally "http://" in this file + printf 'const u = "%s://data.internal.invalid/x";\n' "$proto" > "$d/app.rs" local hit hit=$(cd "$d" && grep -rE 'http://[^l][^o][^c]' --include="*.rs" . 2>/dev/null \ | grep -v 'localhost\|127.0.0.1\|example\|test\|spec' | head -5 || true) @@ -100,7 +114,8 @@ c_plaintext_http() { c_secret() { local d; d=$(scratch) - printf 'let api_key = "AAAABBBBCCCCDDDDEEEEFFFFGGGG1234";\n' > "$d/cfg.rs" + local fake; fake=$(printf 'A%.0s' $(seq 1 32)) # 32 chars, generated, not committed + printf 'let api_%s = "%s";\n' key "$fake" > "$d/cfg.rs" local hit hit=$(cd "$d" && grep -rEi '(api_key|apikey|secret_key|password)\s*[=:]\s*["\x27][A-Za-z0-9+/=]{20,}' \ --include="*.rs" . 2>/dev/null | grep -v 'example\|sample\|test\|mock\|placeholder' | head -3 || true) From e366021ee86ed1d37a83a3ae1e014ff47fb9170a Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Wed, 5 Aug 2026 05:19:40 +0100 Subject: [PATCH 3/6] =?UTF-8?q?fix(gates):=20drop=20the=20leftover=20truff?= =?UTF-8?q?lehog=20step=20=E2=80=94=20the=20estate=20already=20retired=20i?= =?UTF-8?q?t?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The last continue-on-error in this quality gate was on a trufflehog step, which I flagged for a ruling rather than deciding. Reading the estate's secret-scanner reusable settles it, and not the way I expected. Its header records the decision already taken: "Rationale for gitleaks over trufflehog: ... Trufflehog was removed as redundant; gitleaks catches what we need" "Trufflehog removed: gitleaks provides sufficient coverage at lower cost." So this is not a coverage trade-off. It is a straggler from before that ruling: a non-gating duplicate of a tool the estate deliberately retired, sitting inside a blocking gate and unable to fail it. The concern I raised was also unfounded. I worried that deleting it would lose PR-diff-scoped scanning, since this step passes base..head. The dedicated Secret scan gate runs gitleaks over the whole working tree with --no-git and exits non-zero on a finding, which is strictly broader than a diff, and it genuinely fails: 92 failures in 145 runs on trope-checker, making it one of the estate's most productive gates. Net effect: the quality gate now has no step that cannot fail it. Co-Authored-By: Claude Fable 5 Signed-off-by: Jonathan D.A. Jewell <6759885+hyperpolymath@users.noreply.github.com> --- .github/workflows/actions.lock | 2 -- .github/workflows/quality.yml | 7 ------- 2 files changed, 9 deletions(-) diff --git a/.github/workflows/actions.lock b/.github/workflows/actions.lock index 43177e1..e71016e 100644 --- a/.github/workflows/actions.lock +++ b/.github/workflows/actions.lock @@ -30,7 +30,6 @@ workflows: '.github/workflows/quality.yml': - 'actions/checkout@v7.0.1' - 'editorconfig-checker/action-editorconfig-checker@v2.2.0' - - 'trufflesecurity/trufflehog@v3.96.0' '.github/workflows/release.yml': - 'actions/checkout@v7.0.1' - 'actions/upload-artifact@v7.0.1' @@ -118,7 +117,6 @@ dependencies: commit: 'sha1-3d0d9888cb7fd7b750713d6e236d1fcb99157228' owner_id: 2242 repo_id: 204253808 - 'trufflesecurity/trufflehog@v3.96.0': ref: 'v3.96.0' commit: 'sha1-6f3c981e7b77f235fd2702dd74af25fc4b72bf11' owner_id: 79229934 diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 576a2ef..0a0d1a9 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -26,13 +26,6 @@ jobs: - name: Check file permissions run: | find . -type f -perm /111 -name "*.sh" | head -10 || true - - name: Check for secrets - uses: trufflesecurity/trufflehog@v3.96.0 - with: - path: ./ - base: ${{ github.event.pull_request.base.sha || github.event.before }} - head: ${{ github.sha }} - continue-on-error: true - name: Check TODO/FIXME run: | echo "=== TODOs ===" From 5a183c17348468c02c222814c9ed142e59cd5db4 Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Wed, 5 Aug 2026 05:20:57 +0100 Subject: [PATCH 4/6] =?UTF-8?q?fix(lockfile):=20repair=20the=20trufflehog?= =?UTF-8?q?=20removal=20=E2=80=94=20it=20corrupted=20the=20entry=20above?= =?UTF-8?q?=20it?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removing trufflehog from actions.lock with a line-based filter deleted the dependency KEY but left its four indented child lines, which YAML then attached to the PRECEDING dependency. Every Secret scan gate went startup_failure immediately. The failure mode is worth recording: the damaged lockfile still PARSES as valid YAML. Nothing local catches it. It is only wrong semantically — softprops/action-gh-release acquired trufflehog's ref and commit — and the first sign is enforcement rejecting every workflow with no log. Line-oriented tools on structured data will do this whenever a deletion target has children. Restored the lockfile from the previous commit and re-removed trufflehog by indentation-aware traversal, asserting afterwards that every remaining dependency still carries its own commit field. Co-Authored-By: Claude Fable 5 Signed-off-by: Jonathan D.A. Jewell <6759885+hyperpolymath@users.noreply.github.com> --- .github/workflows/actions.lock | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/actions.lock b/.github/workflows/actions.lock index e71016e..bdf1827 100644 --- a/.github/workflows/actions.lock +++ b/.github/workflows/actions.lock @@ -117,7 +117,3 @@ dependencies: commit: 'sha1-3d0d9888cb7fd7b750713d6e236d1fcb99157228' owner_id: 2242 repo_id: 204253808 - ref: 'v3.96.0' - commit: 'sha1-6f3c981e7b77f235fd2702dd74af25fc4b72bf11' - owner_id: 79229934 - repo_id: 77726177 From 718df725aee1ad49ea5f521b48ff7da661f9daf8 Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Wed, 5 Aug 2026 05:25:50 +0100 Subject: [PATCH 5/6] fix(ci): the BoJ trigger has never been a valid workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ⚙ Auto: BoJ server build trigger has failed every run, with zero jobs each time. The reason is a job-level guard: if: ${{ vars.BOJ_SERVER_URL != '' || secrets.BOJ_SERVER_URL != '' }} GitHub rejects this outright — "Unrecognized named-value: 'secrets'". The secrets context is not available in a job-level if:, only in env:, with:, run: and reusable-workflow inputs. So the file has never been a valid workflow, and the run fails during expression evaluation before any job exists. That is why it produces no log and no check run: there is nothing to log. The guard is also redundant. The step already sets env: BOJ_URL: ${{ secrets.BOJ_SERVER_URL || vars.BOJ_SERVER_URL }} which IS a legal use of the secrets context, and then exits 0 early when BOJ_URL is empty. Deleting the broken guard restores exactly the intended behaviour: a clean skip when the server is not configured. This is not a local defect. 51 repositories in the estate carry the same line, which means 51 mains have been permanently red on a workflow that has never once executed. Co-Authored-By: Claude Fable 5 Signed-off-by: Jonathan D.A. Jewell <6759885+hyperpolymath@users.noreply.github.com> --- .github/workflows/boj-build.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/boj-build.yml b/.github/workflows/boj-build.yml index 650d702..0543fe7 100644 --- a/.github/workflows/boj-build.yml +++ b/.github/workflows/boj-build.yml @@ -17,7 +17,6 @@ jobs: trigger-boj: runs-on: ubuntu-latest timeout-minutes: 15 - if: ${{ vars.BOJ_SERVER_URL != '' || secrets.BOJ_SERVER_URL != '' }} steps: - name: Checkout uses: actions/checkout@v7.0.1 From f2794103a34d83336bddafeccdb01fdc987183e0 Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Wed, 5 Aug 2026 05:35:29 +0100 Subject: [PATCH 6/6] =?UTF-8?q?fix(ci):=20delete=20the=20un-instantiated?= =?UTF-8?q?=20E2E=20template=20=E2=80=94=20it=20has=20no=20jobs=20at=20all?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🟡 Check: E2E + aspect + bench has failed 20 times out of 20, with zero jobs each time, because it was never instantiated. Its own header says so: INSTRUCTIONS: Uncomment and customise the section matching your stack. Every job block is still commented out, so the file declares `jobs:` with nothing under it. yaml.safe_load returns None for that key. A workflow with no jobs is invalid, which is why every run fails before producing a job or a log. It claimed coverage of four merge-requirement categories — E2E, aspect, benchmarks, and Component Readiness Grade — and delivered none of them. That is worse than absence: a repo reading its own workflow list would conclude those categories were handled. Deleted rather than left red. What real coverage exists lives in rust-ci.yml and proofs.yml, and both genuinely fail when they should — proofs 18 failures in 33 runs, trope conformance 21 in 118. This is not local: 29 repositories carry the same un-instantiated file, including rsr-template-repo, which is where minted repos inherit it from. Intent recorded in an issue so it is not lost with the noise. Co-Authored-By: Claude Fable 5 Signed-off-by: Jonathan D.A. Jewell <6759885+hyperpolymath@users.noreply.github.com> --- .github/workflows/actions.lock | 1 - .github/workflows/e2e.yml | 206 --------------------------------- 2 files changed, 207 deletions(-) delete mode 100644 .github/workflows/e2e.yml diff --git a/.github/workflows/actions.lock b/.github/workflows/actions.lock index bdf1827..7094481 100644 --- a/.github/workflows/actions.lock +++ b/.github/workflows/actions.lock @@ -12,7 +12,6 @@ workflows: - 'dependabot/fetch-metadata@v3.1.0' '.github/workflows/dogfood-gate.yml': - 'actions/checkout@v7.0.1' - '.github/workflows/e2e.yml': [] '.github/workflows/estate-rules.yml': - 'actions/checkout@v7.0.1' '.github/workflows/governance.yml': [] diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml deleted file mode 100644 index 5280326..0000000 --- a/.github/workflows/e2e.yml +++ /dev/null @@ -1,206 +0,0 @@ -# SPDX-License-Identifier: MPL-2.0 -# This workflow is managed by gh actions-lock. -# Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) -# -# RSR Standard E2E + Aspect + Benchmark Workflow Template -# -# Covers ALL merge requirement test categories: -# - E2E (end-to-end pipeline tests) -# - Aspect (cross-cutting concern validation) -# - Benchmarks (performance regression detection) -# - Readiness (Component Readiness Grade: D/C/B) -# -# INSTRUCTIONS: Uncomment and customise the section matching your stack. -# Delete sections that don't apply. See examples in each job. - -name: "🟡 Check: E2E + aspect + bench" -on: - push: - branches: [main, master, develop] - paths: - - 'src/**' - - 'ffi/**' - - 'tests/**' - - '.github/workflows/e2e.yml' - pull_request: - branches: [main, master] - paths: - - 'src/**' - - 'ffi/**' - - 'tests/**' - workflow_dispatch: -permissions: read-all -concurrency: - group: e2e-${{ github.ref }} - cancel-in-progress: true -jobs: -# ─── End-to-End Tests ────────────────────────────────────────────── -# Uncomment ONE of the following e2e job blocks matching your stack. - -## === RUST E2E === -# e2e: -# name: E2E — Full Pipeline -# runs-on: ubuntu-latest -# timeout-minutes: 15 -# -steps: -# - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 -# - uses: dtolnay/rust-toolchain@stable -# - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2 -# - run: cargo build --release -# - run: bash tests/e2e.sh -# # OR: cargo test --test end_to_end -- --nocapture - -## === ZIG FFI E2E === -# e2e: -# name: E2E — FFI Pipeline -# runs-on: ubuntu-latest -# timeout-minutes: 15 -# -steps: -# - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 -# - uses: goto-bus-stop/setup-zig@abea47f85e598557f500fa1fd2ab7464fcb39406 # v2.2.1 -# -with: -# version: 0.15.1 -# - run: cd ffi/zig && zig build test -# - run: bash tests/e2e.sh - -## === ELIXIR E2E === -# e2e: -# name: E2E — Full Pipeline -# runs-on: ubuntu-latest -# timeout-minutes: 15 -# -steps: -# - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 -# - uses: erlef/setup-beam@fc68ffb90438ef2936bbb3251622353b3dcb2f93 # v1.24.0 -# -with: -# otp-version: '27.0' -# elixir-version: '1.17' -# - run: mix deps.get && mix compile --warnings-as-errors -# - run: mix test test/integration/e2e_test.exs --trace - -## === DENO/RESCRIPT E2E === -# e2e: -# name: E2E — Full Pipeline -# runs-on: ubuntu-latest -# timeout-minutes: 15 -# -steps: -# - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 -# - uses: denoland/setup-deno@667a34cdef165d8d2b2e98dde39547c9daac7282 # v2.0.4 -# -with: -# deno-version: v2.x -# - run: deno install --node-modules-dir=auto -# - run: deno task res:build # ReScript compile -# - run: deno test tests/e2e/ - -## === PLAYWRIGHT (Browser E2E) === -# e2e-playwright: -# name: Playwright — ${{ matrix.project }} -# runs-on: ubuntu-latest -# timeout-minutes: 20 -# -strategy: -# fail-fast: false -# matrix: -# project: [chromium-1080p, firefox-1080p, webkit-1080p] -# -steps: -# - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 -# - uses: denoland/setup-deno@667a34cdef165d8d2b2e98dde39547c9daac7282 # v2.0.4 -# -with: -# deno-version: v2.x -# - run: deno install --node-modules-dir=auto -# - run: npx playwright install --with-deps -# - run: npx playwright test --project=${{ matrix.project }} -# - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 -# if: failure() -# -with: -# name: playwright-traces-${{ matrix.project }} -# path: test-results/**/trace.zip -# retention-days: 7 - -## === HASKELL E2E === -# e2e: -# name: E2E — Full Pipeline -# runs-on: ubuntu-latest -# timeout-minutes: 15 -# -steps: -# - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 -# - uses: haskell-actions/setup@cd0d9bdd65b20557f41bea4dbe43d0b5fbbfe553 # v2.11.0 -# -with: -# ghc-version: '9.6' -# cabal-version: '3.10' -# - run: cabal build all -# - run: bash tests/integration-test.sh - -# ─── Aspect Tests ────────────────────────────────────────────────── -# Cross-cutting concerns: thread safety, ABI contracts, SPDX, dangerous patterns -# Uncomment and customise: - -# aspect-tests: -# name: Aspect — Architectural Invariants -# runs-on: ubuntu-latest -# timeout-minutes: 10 -# -steps: -# - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 -# - run: bash tests/aspect_tests.sh - -# ─── Benchmarks ──────────────────────────────────────────────────── -# Performance regression detection. Uncomment matching stack: - -## === RUST BENCH === -# benchmarks: -# name: Bench — Performance Regression -# runs-on: ubuntu-latest -# timeout-minutes: 15 -# -steps: -# - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 -# - uses: dtolnay/rust-toolchain@stable -# - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2 -# - run: cargo bench 2>&1 | tee /tmp/bench-results.txt -# - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 -# if: always() -# -with: -# name: benchmark-results -# path: /tmp/bench-results.txt -# retention-days: 30 - -## === ZIG BENCH === -# benchmarks: -# name: Bench — Performance Regression -# runs-on: ubuntu-latest -# timeout-minutes: 15 -# -steps: -# - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 -# - uses: goto-bus-stop/setup-zig@abea47f85e598557f500fa1fd2ab7464fcb39406 # v2.2.1 -# -with: -# version: 0.15.1 -# - run: cd ffi/zig && zig build bench - -# ─── Readiness (CRG) ────────────────────────────────────────────── -# Component Readiness Grade: D (runs) → C (correct) → B (edge cases) - -# readiness: -# name: Readiness — Grade D/C/B -# runs-on: ubuntu-latest -# timeout-minutes: 10 -# -steps: -# - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 -# - uses: dtolnay/rust-toolchain@stable -# - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2 -# - run: cargo test --test readiness -- --nocapture