From 92311989e4e270769b8ca27fa78e601d0e497637 Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Wed, 5 Aug 2026 01:57:07 +0100 Subject: [PATCH 1/5] 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 An estate-wide scan (>=30 lifetime runs, zero lifetime failures, taken from the API's own failure count) found this repo's 🔴 Gate: checks have never failed in 100-120 runs each. Reading them explains why: - 🔴 Gate: Guix-only package policy had NO exit path at all. It printed a warning for foreign lock files and always succeeded. A gate named 'Guix-only' enforced nothing. - 🔴 Gate: Security policy present detected three things and failed on one. Weak crypto (MD5/SHA1) and plaintext HTTP printed a warning and passed. - 🔴 Gate: Code quality had continue-on-error on the EditorConfig step. These are the repos that adopted the tier taxonomy most carefully, which makes a fake gate here worse than elsewhere: the red label suppresses the scrutiny that would otherwise find it. Changes: weak crypto and plaintext HTTP now fail the security gate; a foreign lock file now fails the Guix-only gate; EditorConfig no longer continues on error. And per standards R10 (canonical wrongness), a gate that has never failed is indistinguishable from one that CANNOT fail, so the claim is now demonstrated rather than asserted: scripts/test-gate-falsifiability.sh plants deliberately-wrong input in a scratch tree for each detection and asserts it is REJECTED, plus a fifth canary asserting clean input is NOT rejected (a suite that always fires proves nothing). Wired into the quality gate. Worth recording: the first version of the plaintext-HTTP canary used example.net and did NOT fire, because the gate excludes anything containing 'example'. The fixture was not actually wrong — exactly the silent failure R10 exists to catch, found by the canary on its first run. Noted in-file as a caution. EXPECT RED. These gates have been passing because they could not fail; some of what they now catch will be real and long-standing. Co-Authored-By: Claude Fable 5 Signed-off-by: Jonathan D.A. Jewell <6759885+hyperpolymath@users.noreply.github.com> --- .github/workflows/guix-nix-policy.yml | 11 ++- .github/workflows/quality.yml | 4 +- .github/workflows/security-policy.yml | 6 +- scripts/test-gate-falsifiability.sh | 102 ++++++++++++++++++++++++++ 4 files changed, 118 insertions(+), 5 deletions(-) create mode 100755 scripts/test-gate-falsifiability.sh diff --git a/.github/workflows/guix-nix-policy.yml b/.github/workflows/guix-nix-policy.yml index 25bd916..d52f21d 100644 --- a/.github/workflows/guix-nix-policy.yml +++ b/.github/workflows/guix-nix-policy.yml @@ -24,6 +24,7 @@ jobs: - uses: actions/checkout@v7.0.1 - name: Enforce Guix primary / Nix fallback run: | + FAILED=false # Check for package manager files HAS_GUIX=$(find . -name "*.scm" -o -name ".guix-channel" -o -name "guix.scm" 2>/dev/null | head -1) HAS_NIX=$(find . -name "*.nix" 2>/dev/null | head -1) @@ -31,7 +32,9 @@ jobs: # Block new package-lock.json, yarn.lock, Gemfile.lock, etc. NEW_LOCKS=$(git diff --name-only --diff-filter=A HEAD~1 2>/dev/null | grep -E 'package-lock\.json|yarn\.lock|Gemfile\.lock|Pipfile\.lock|poetry\.lock|cargo\.lock' || true) if [ -n "$NEW_LOCKS" ]; then - echo "âš ī¸ Lock files detected. Prefer Guix manifests for reproducibility." + echo "❌ Foreign lock files added — this repo is Guix-only:" + echo "$NEW_LOCKS" + FAILED=true fi # Prefer Guix, fallback to Nix @@ -43,4 +46,8 @@ jobs: echo "â„šī¸ Consider adding guix.scm or flake.nix for reproducible builds" fi - echo "✅ Package policy check passed" + if [ "$FAILED" = true ]; then + echo "Guix-only package policy violated — see above." + exit 1 + fi + echo "✅ Guix-only package policy satisfied" diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index caf3af7..5a52d23 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -42,7 +42,9 @@ 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/scripts/test-gate-falsifiability.sh b/scripts/test-gate-falsifiability.sh new file mode 100755 index 0000000..cc269ee --- /dev/null +++ b/scripts/test-gate-falsifiability.sh @@ -0,0 +1,102 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: PMPL-1.0-or-later +# +# 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 in a scratch tree, runs the gate's own detection 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 + +PASS=0; FAIL=0 +ok() { echo "PASS $*"; PASS=$((PASS+1)); } +bad() { echo "FAIL $*"; FAIL=$((FAIL+1)); } + +scratch() { mktemp -d "${TMPDIR:-/tmp}/haec-canary.XXXXXX"; } + +# --- canary 1: weak crypto must be rejected ------------------------------- +c1() { + local d; d=$(scratch); trap 'rm -rf "$d"' RETURN + 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) + if [ -n "$hit" ]; then + ok "weak crypto (md5) is detected" + else + bad "weak crypto NOT detected — the security gate is blind to it" + fi +} + +# --- canary 2: plaintext HTTP must be rejected ---------------------------- +# 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. +c2() { + local d; d=$(scratch); trap 'rm -rf "$d"' RETURN + 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) + if [ -n "$hit" ]; then + ok "plaintext HTTP is detected" + else + bad "plaintext HTTP NOT detected — the security gate is blind to it" + fi +} + +# --- canary 3: hardcoded secrets must be rejected ------------------------- +c3() { + local d; d=$(scratch); trap 'rm -rf "$d"' RETURN + 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) + if [ -n "$hit" ]; then + ok "hardcoded secret is detected" + else + bad "hardcoded secret NOT detected — the security gate is blind to it" + fi +} + +# --- canary 4: a foreign lock file must be rejected ----------------------- +c4() { + local hit + hit=$(printf 'package-lock.json\nsrc/main.rs\n' \ + | grep -E 'package-lock\.json|yarn\.lock|Gemfile\.lock|Pipfile\.lock|poetry\.lock|cargo\.lock' || true) + if [ -n "$hit" ]; then + ok "foreign lock file is detected" + else + bad "foreign lock file NOT detected — the Guix-only gate is blind to it" + fi +} + +# --- canary 5: clean input must NOT be rejected --------------------------- +# Guards the opposite error: a canary suite that always fires proves nothing. +c5() { + local d; d=$(scratch); trap 'rm -rf "$d"' RETURN + printf 'fn h() { sha256(x) }\nconst u = "https://ok.invalid/x";\n' > "$d/clean.rs" + local hit + hit=$(cd "$d" && grep -rE 'md5\(|sha1\(' --include="*.rs" . 2>/dev/null | head -1 || true) + if [ -z "$hit" ]; then + ok "clean input is not falsely rejected" + else + bad "clean input WAS rejected — the gate produces false positives" + fi +} + +c1; c2; c3; c4; c5 +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 bcba81cc66c77aa242fd38bd163ca86244c49a9d 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:30 +0100 Subject: [PATCH 2/5] =?UTF-8?q?fix(canaries):=20drop=20the=20RETURN=20trap?= =?UTF-8?q?=20=E2=80=94=20it=20fired=20again=20with=20$d=20out=20of=20scop?= =?UTF-8?q?e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `trap ... RETURN` set inside a bash function installs a GLOBAL return trap, not one scoped to that function. Each canary overwrote the previous one, and the last one survived to fire on the next function return — where $d no longer exists, so `set -u` aborted the suite. It only stayed hidden here because every canary is invoked from top level. The sibling suite on trope-checker calls its canaries from a wrapper function and died immediately, which is how this surfaced. Replaced with explicit cleanup after the value under test is captured. The suite now exits 0 with every canary firing. Co-Authored-By: Claude Fable 5 Signed-off-by: Jonathan D.A. Jewell <6759885+hyperpolymath@users.noreply.github.com> --- scripts/test-gate-falsifiability.sh | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/scripts/test-gate-falsifiability.sh b/scripts/test-gate-falsifiability.sh index cc269ee..b59eefd 100755 --- a/scripts/test-gate-falsifiability.sh +++ b/scripts/test-gate-falsifiability.sh @@ -22,11 +22,12 @@ scratch() { mktemp -d "${TMPDIR:-/tmp}/haec-canary.XXXXXX"; } # --- canary 1: weak crypto must be rejected ------------------------------- c1() { - local d; d=$(scratch); trap 'rm -rf "$d"' RETURN + 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 @@ -40,11 +41,12 @@ c1() { # 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. c2() { - local d; d=$(scratch); trap 'rm -rf "$d"' RETURN + 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 @@ -54,11 +56,12 @@ c2() { # --- canary 3: hardcoded secrets must be rejected ------------------------- c3() { - local d; d=$(scratch); trap 'rm -rf "$d"' RETURN + 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 @@ -81,10 +84,11 @@ c4() { # --- canary 5: clean input must NOT be rejected --------------------------- # Guards the opposite error: a canary suite that always fires proves nothing. c5() { - local d; d=$(scratch); trap 'rm -rf "$d"' RETURN + local d; d=$(scratch) printf 'fn h() { sha256(x) }\nconst u = "https://ok.invalid/x";\n' > "$d/clean.rs" local hit hit=$(cd "$d" && grep -rE 'md5\(|sha1\(' --include="*.rs" . 2>/dev/null | head -1 || true) + rm -rf "$d" if [ -z "$hit" ]; then ok "clean input is not falsely rejected" else From d16cefe670ce256ee3defc56fb8913fc0270be6e 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:49 +0100 Subject: [PATCH 3/5] 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 b59eefd..371598e 100755 --- a/scripts/test-gate-falsifiability.sh +++ b/scripts/test-gate-falsifiability.sh @@ -12,6 +12,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 PASS=0; FAIL=0 @@ -23,7 +35,8 @@ scratch() { mktemp -d "${TMPDIR:-/tmp}/haec-canary.XXXXXX"; } # --- canary 1: weak crypto must be rejected ------------------------------- c1() { 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) @@ -42,7 +55,8 @@ c1() { # is the exact failure R10 exists to catch. Kept as a caution. c2() { 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) @@ -57,7 +71,8 @@ c2() { # --- canary 3: hardcoded secrets must be rejected ------------------------- c3() { 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 fecc9f8ce8b636ece0d06374618d365465e0cea2 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:37 +0100 Subject: [PATCH 4/5] =?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 03a4ddf..4127727 100644 --- a/.github/workflows/actions.lock +++ b/.github/workflows/actions.lock @@ -31,7 +31,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/download-artifact@v8.0.1' @@ -125,7 +124,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 5a52d23..155fc1f 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 5e259ec0b3226eb8406a900cfdadc432c835fca7 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:54 +0100 Subject: [PATCH 5/5] =?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 4127727..c47fb05 100644 --- a/.github/workflows/actions.lock +++ b/.github/workflows/actions.lock @@ -124,7 +124,3 @@ dependencies: commit: 'sha1-3d0d9888cb7fd7b750713d6e236d1fcb99157228' owner_id: 2242 repo_id: 204253808 - ref: 'v3.96.0' - commit: 'sha1-6f3c981e7b77f235fd2702dd74af25fc4b72bf11' - owner_id: 79229934 - repo_id: 77726177