From 982ea3713adab31734a32dedc48e2d2c17325f2b Mon Sep 17 00:00:00 2001 From: hyperpolymath Date: Tue, 22 Sep 2026 10:11:12 +0000 Subject: [PATCH 1/2] fix(main-red-round2): restore asana lock, fail-closed validator, self-lint baseline step MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - actions.lock: restore asana/push-signed-commits (usage + metadata, verbatim) — #947 deleted it to silence Hypatia, but the composite action .github/actions/signed-push really uses it (uses-lock failed). The transitive setup-python@v2 finding is acked honestly in the baseline instead (next commit). - hypatia-scan-reusable.yml: revert the validator to fail-closed (length > 0); an empty scan is a scanner error, never a clean scan. The #741 control tested the pre-#771 slurp accident; science-ci pins empty-exit-2 too. - scripts/tests/hypatia-blocking-gate-test.sh: 'empty findings are valid' becomes 'empty findings refuse (fail-closed)', expect 2. - governance-reusable.yml: baseline-validation step prefers the caller's own scripts/apply-baseline.sh (self-lint); main-pinned fallback for consumers. Fixes the stale-script trap where main's old script rejected new severities (warn). - .githooks/validate-actions-lock.sh: remove the stale deno EXPECTED_ABSENT exception (last consumer gone); guard the loops for the empty array (bash 3.2 + set -u safe). --- .githooks/validate-actions-lock.sh | 13 ++++++------- .github/workflows/actions.lock | 8 ++++++++ .github/workflows/governance-reusable.yml | 12 +++++++++++- .github/workflows/hypatia-scan-reusable.yml | 12 +++++++----- scripts/tests/hypatia-blocking-gate-test.sh | 2 +- 5 files changed, 33 insertions(+), 14 deletions(-) diff --git a/.githooks/validate-actions-lock.sh b/.githooks/validate-actions-lock.sh index 4566a7168..8f2d56a47 100755 --- a/.githooks/validate-actions-lock.sh +++ b/.githooks/validate-actions-lock.sh @@ -63,11 +63,10 @@ RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m' # quietly widen to cover an accident. An entry that stops being used is # reported as stale, so it cannot rot either. EXPECTED_ABSENT=( - # Estate doctrine is bun-only; deno is banned. Keying it would make a - # BANNED runtime a required lockfile key for every caller of - # governance-reusable.yml. The cure is to remove the consumer -- port the - # governance scripts to bun -- not to satisfy it. - "denoland/setup-deno@22d081ff2d3a40755e97629de92e3bcbfa7cf2ed" + # Empty: the denoland/setup-deno exception was removed 2026-09-22 after + # the last consumer went away (the validator itself reported it stale). + # Estate doctrine is still bun-only; if a deno ref ever returns, the + # cure is to remove the consumer, not to re-add an exception. ) if [ ! -f "$LOCKFILE" ]; then @@ -154,7 +153,7 @@ for ref in "${RAW[@]}"; do norm="$(printf '%s/%s@%s' "$owner" "$repo" "$sha" | tr '[:upper:]' '[:lower:]')" skip=0 - for ex in "${EXPECTED_ABSENT[@]}"; do + for ex in ${EXPECTED_ABSENT[@]+"${EXPECTED_ABSENT[@]}"}; do ex_lc="$(printf '%s' "$ex" | tr '[:upper:]' '[:lower:]')" if [ "$norm" = "$ex_lc" ]; then SEEN_ABSENT+=("$ex_lc") @@ -174,7 +173,7 @@ done # A doctrine exception that is no longer used must be removed, or the list # becomes a place where real coverage gaps can hide. -for ex in "${EXPECTED_ABSENT[@]}"; do +for ex in ${EXPECTED_ABSENT[@]+"${EXPECTED_ABSENT[@]}"}; do ex_lc="$(printf '%s' "$ex" | tr '[:upper:]' '[:lower:]')" found=0 for s in ${SEEN_ABSENT[@]+"${SEEN_ABSENT[@]}"}; do diff --git a/.github/workflows/actions.lock b/.github/workflows/actions.lock index 83a8fd4cb..237d86039 100644 --- a/.github/workflows/actions.lock +++ b/.github/workflows/actions.lock @@ -139,6 +139,7 @@ workflows: '.github/workflows/signed-push-smoke.yml': - 'actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1' - 'actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1' + - 'asana/push-signed-commits@d615ca88d8e1a946734c24970d1e7a6c56f34897' '.github/workflows/spark-theatre-gate.yml': - 'actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1' '.github/workflows/tag-ruleset-canon.yml': @@ -200,6 +201,13 @@ dependencies: repo_id: 496012378 uses: - 'actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f' + 'asana/push-signed-commits@d615ca88d8e1a946734c24970d1e7a6c56f34897': + ref: 'v1.3' + commit: 'sha1-d615ca88d8e1a946734c24970d1e7a6c56f34897' + owner_id: 1472111 + repo_id: 772313726 + uses: + - 'actions/setup-python@v2' 'dtolnay/rust-toolchain@02cb101ec7c40f2c49e1d9714d64511d8e1b74de': ref: '02cb101ec7c40f2c49e1d9714d64511d8e1b74de' commit: 'sha1-02cb101ec7c40f2c49e1d9714d64511d8e1b74de' diff --git a/.github/workflows/governance-reusable.yml b/.github/workflows/governance-reusable.yml index 3c8c48aaa..19d028a98 100644 --- a/.github/workflows/governance-reusable.yml +++ b/.github/workflows/governance-reusable.yml @@ -273,7 +273,17 @@ jobs: # checked out to get apply-baseline.sh) is itself scanned, and # standards' own files get reported as the caller's findings (a banned # `.ts`, `shell_download` bootstrap.sh scripts, etc.). - cp .standards-checkout/scripts/apply-baseline.sh "$RUNNER_TEMP/apply-baseline.sh" + # Prefer the CALLER's own scripts/apply-baseline.sh when present + # (self-lint: standards validating itself must run the tree under + # test, not main's copy — a new baseline severity the main-pinned + # script doesn't know would fail closed here while passing + # everywhere else). Consumers without the script keep the + # main-pinned fallback. + if [ -f scripts/apply-baseline.sh ]; then + cp scripts/apply-baseline.sh "$RUNNER_TEMP/apply-baseline.sh" + else + cp .standards-checkout/scripts/apply-baseline.sh "$RUNNER_TEMP/apply-baseline.sh" + fi rm -rf .standards-checkout # hypatia's `scan` exits non-zero whenever it finds anything — that is # by design, and under `bash -e` it would abort this step at this line, diff --git a/.github/workflows/hypatia-scan-reusable.yml b/.github/workflows/hypatia-scan-reusable.yml index 33befadd0..ba4bf19ab 100644 --- a/.github/workflows/hypatia-scan-reusable.yml +++ b/.github/workflows/hypatia-scan-reusable.yml @@ -126,12 +126,14 @@ jobs: run: | set -euo pipefail # Exactly one JSON array of findings, each with a recognised severity. - # Missing/truncated output is a scanner error; an empty array is a - # clean scan (the `length > 0` the #771 rewrite added broke the - # `empty findings are valid` control — #741/#742 always treated an - # empty scan as valid). + # Missing/truncated output is a scanner error, never an empty clean + # scan: a scanner that emits nothing is indistinguishable from a + # crashed/truncated run, so the gate fails closed (science-ci- + # security-test.rb pins this too). The #741 `empty findings are + # valid` control tested the pre-#771 slurp accident (`[[]]` has + # length 1); #771's `length > 0` is the documented intent. if [ ! -s hypatia-findings.json ] || ! jq -e ' - type == "array" and all(.[]; + type == "array" and length > 0 and all(.[]; type == "object" and (.severity as $s | ["critical", "high", "medium", "warn", "low", "info", "informational"] | index($s) != null)) ' hypatia-findings.json >/dev/null; then diff --git a/scripts/tests/hypatia-blocking-gate-test.sh b/scripts/tests/hypatia-blocking-gate-test.sh index 031a9f78d..e0680b656 100755 --- a/scripts/tests/hypatia-blocking-gate-test.sh +++ b/scripts/tests/hypatia-blocking-gate-test.sh @@ -43,7 +43,7 @@ check() { fi printf 'PASS: %s\n' "$name" } -check 'empty findings are valid' 0 '[]' +check 'empty findings refuse (fail-closed)' 2 '[]' check 'low and informational findings pass' 0 '[{"severity":"low"},{"severity":"info"}]' check 'high finding blocks' 1 '[{"severity":"high"}]' check 'critical finding blocks' 1 '[{"severity":"critical"}]' From e60e3ad61ec57f518af5ca8bb9064ce62c6da824 Mon Sep 17 00:00:00 2001 From: hyperpolymath Date: Tue, 22 Sep 2026 10:11:12 +0000 Subject: [PATCH 2/2] chore(main-red-round2): ack asana transitive, re-baseline debt counts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - .hypatia-baseline.json: 210 -> 211. Ack workflow_audit/ invalid_actions_lock (**actions.lock): asana's setup-python@v2 transitive floats upstream; gh verify says valid:true, Hypatia is stricter. Tracked in #951. - Debtfile: deno-residue probe counts non-comment matches only (3 retirement-doc comments are not residue); count 2 -> 0, ceiling holds at 1. - Debtfile: re-baseline fossilized counts to measured (#953): gate-scripts-without-tests 31/30 -> 40/40, todo-fixme-markers 77/76 -> 80/80. The runner only ratchets down; #820's own tree already measured 38/79. Ratchet-exception: .hypatia-baseline.json — one honest ack for the asana float-transitive (see #951); the growth is the ack, not new debt. Debt-exception: gate-scripts-without-tests — re-baselining fossilized 31/30 to measured 40/40 (#953); falls as tests land. Debt-exception: todo-fixme-markers — re-baselining fossilized 77/76 to measured 80/80 (#953); advisory markers only. --- .hypatia-baseline.json | 9 +++++++++ .machine_readable/Debtfile.a2ml | 18 +++++++++--------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/.hypatia-baseline.json b/.hypatia-baseline.json index 1d39cd7a9..9c7481e2e 100644 --- a/.hypatia-baseline.json +++ b/.hypatia-baseline.json @@ -1723,5 +1723,14 @@ "note": "TRACKED DEBT (hypatia triage 2026-09-22): plain-HTTP URL in prose; needs per-instance eyes (protocol examples may be intentional) before flipping to https.", "expires_at": "2026-12-22", "tracking_issue": "hyperpolymath/standards#937" + }, + { + "severity": "high", + "rule_module": "workflow_audit", + "type": "invalid_actions_lock", + "file_pattern": "**actions.lock", + "note": "TRIAGED (round-2 fix-forward 2026-09-22): Hypatia workflow_audit fails the lock closed with transitive_dependencies_missing -- asana/push-signed-commits@d615 (immutable pin, ref v1.3) declares transitive actions/setup-python@v2, which floats upstream and can never be a lock key. gh-actions-lock v0.1.6 verify says valid:true on the same file; Hypatia is stricter than the authoritative tool. Exposure is upstream float only. Exit: Asana pins setup-python, we replace the action, or Hypatia downgrades float-transitives. #947 deleted the metadata to silence the scanner; round 2 restored it verbatim and acked honestly.", + "expires_at": "2026-12-22", + "tracking_issue": "hyperpolymath/standards#951" } ] diff --git a/.machine_readable/Debtfile.a2ml b/.machine_readable/Debtfile.a2ml index 6c19672c1..2fc0c1297 100644 --- a/.machine_readable/Debtfile.a2ml +++ b/.machine_readable/Debtfile.a2ml @@ -37,10 +37,10 @@ forgotten. ## Test coverage of the gates ### gate-scripts-without-tests -- description: Scripts under scripts/ with no matching scripts/tests/-test.sh — a gate with no test has never been shown able to fail +- description: Scripts under scripts/ with no matching scripts/tests/-test.sh — a gate with no test has never been shown able to fail Re-baselined 2026-09-22 (round 2, issue #953): the committed count had fossilized at 31 while the tree measured 38+; set to measured 40 with a declared ceiling raise. Falls automatically as tests land. - probe: n=0; for f in $(git ls-files 'scripts/*.sh'); do b=$(basename "$f" .sh); case "$b" in *-test) continue;; esac; if [ ! -f "scripts/tests/${b}-test.sh" ] && [ ! -f "scripts/tests/${b#check-}-test.sh" ] && [ ! -f "scripts/tests/${b#run-}-test.sh" ]; then n=$((n+1)); fi; done; echo "$n" -- count: 31 -- ceiling: 30 +- count: 40 +- ceiling: 40 - severity: high - policy: remediable - tri: eliminate @@ -59,10 +59,10 @@ forgotten. - accepted-until: 2026-10-01 ### todo-fixme-markers -- description: Files carrying a TODO or FIXME marker. Advisory — a marker is a note to self, not a defect +- description: Files carrying a TODO or FIXME marker. Advisory — a marker is a note to self, not a defect Re-baselined 2026-09-22 (round 2, issue #953): fossilized at 77, measured 80. - probe: git grep -I -l -E '\b(TODO|FIXME)\b' -- . ':(exclude).machine_readable/Debtfile.a2ml' ':(exclude)docs/DEBTFILE-SPEC.adoc' | wc -l -- count: 77 -- ceiling: 76 +- count: 80 +- ceiling: 80 - severity: low - policy: remediable - tri: control @@ -100,9 +100,9 @@ forgotten. - accepted-until: 2027-01-01 ### deno-residue -- description: Deno residue in this repository after the Bun ruling. The required JS/TS gate is now `scripts/check-ts-allowlist.sh` (bash + awk); `scripts/check-ts-allowlist.deno.js` is RETAINED DELIBERATELY as a compatibility shim, not as residue. governance-reusable fetches `scripts/` at floating `ref: main` while consumers pin the workflow YAML, so deleting the shim breaks every consumer whose pinned YAML still invokes it — MEASURED 2026-09-04 at 269 repos. Owner ruled Deno REMOVED and Bun permanent (said three times, reaffirmed 2026-08-07). Must reach 0, but only via the three-phase retirement: shim (done, PR #730) -> repin consumers (task #59) -> delete. The single remaining probe hit is a COMMENT inside the shim, not a live invocation. Excludes */bindings/deno/, which is interop for OTHER people's Deno code and a separate question. -- probe: git grep -lE "denoland/setup-deno|deno run|deno test|deno fmt|deno lint" -- ".github/workflows/*.yml" "scripts/*" | wc -l -- count: 2 +- description: Deno residue in this repository after the Bun ruling. The required JS/TS gate is now `scripts/check-ts-allowlist.sh` (bash + awk); `scripts/check-ts-allowlist.deno.js` is RETAINED DELIBERATELY as a compatibility shim, not as residue. governance-reusable fetches `scripts/` at floating `ref: main` while consumers pin the workflow YAML, so deleting the shim breaks every consumer whose pinned YAML still invokes it — MEASURED 2026-09-04 at 269 repos. Owner ruled Deno REMOVED and Bun permanent (said three times, reaffirmed 2026-08-07). Must reach 0, but only via the three-phase retirement: shim (done, PR #730) -> repin consumers (task #59) -> delete. The probe counts non-comment matches only (refined 2026-09-22: the three remaining mentions — two workflow retirement notes plus one comment inside the shim — are documentation, not residue, and a probe that punishes documentation is a broken metric). Excludes */bindings/deno/, which is interop for OTHER people's Deno code and a separate question. +- probe: git grep -hE "denoland/setup-deno|deno run|deno test|deno fmt|deno lint" -- ".github/workflows/*.yml" "scripts/*" | grep -vE '^\s*(#|//)' | wc -l +- count: 0 - ceiling: 1 - severity: medium - policy: remediable