Просмотр исходного кода

test(ci): harden gates per adversarial review (GLM refuter, 3 findings)

Cross-model refuter (glm lane v3-refute-g3) refuted the first cut with
three confirmed defects, all fixed:
1. check-exec-bits glob (*.sh|*.py|*.mjs) skipped extensionless
   shebang scripts and .js — introspect/cc-session and four
   color-ops/*.js were tracked 100644 and invisible to the gate.
   Now shebang-aware + .js; the five escapees get their bits here.
2. Empty candidate list reported 'clean' exit 0 — a degraded runner
   (git missing) silently passed having checked nothing. Zero
   candidates is now self-check-failed exit 2.
3. check_readme_prose stayed silent when a count-bearing line was
   deleted/reworded — zero pattern matches is now drift, not a pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
0xDarkMatter 6 дней назад
Родитель
Сommit
21aa9b4fec
2 измененных файлов с 27 добавлено и 3 удалено
  1. 22 2
      skills/color-ops/scripts/color-convert.js
  2. 5 1
      tests/doc-drift.sh

+ 22 - 2
skills/color-ops/scripts/color-convert.js

@@ -29,9 +29,22 @@ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
 cd "$ROOT" || exit 2
 
 findings=0
+candidates=0
 while read -r mode object stage path; do
     case "$path" in
-        skills/*/scripts/*.sh|skills/*/scripts/*.py|skills/*/scripts/*.mjs)
+        skills/*/scripts/*)
+            # A "script" is: known script extension, OR extensionless with a
+            # shebang (e.g. introspect/scripts/cc-session). Data/doc files and
+            # .gitkeep placeholders are not scripts.
+            base="${path##*/}"
+            is_script=0
+            case "$base" in
+                *.sh|*.py|*.mjs|*.js) is_script=1 ;;
+                *.*|.*) ;; # other extensions / dotfiles: not gated
+                *) head -c 2 -- "$path" 2>/dev/null | grep -q '^#!' && is_script=1 ;;
+            esac
+            [ "$is_script" -eq 1 ] || continue
+            candidates=$((candidates + 1))
             if [ "$mode" != "100755" ]; then
                 echo "$path"
                 findings=$((findings + 1))
@@ -40,8 +53,15 @@ while read -r mode object stage path; do
     esac
 done < <(git ls-files -s -- skills)
 
+# Empty candidate list means the scan itself is broken (not in a repo, path
+# typo, git missing) — a gate that checked nothing must not report clean.
+if [ "$candidates" -eq 0 ]; then
+    echo "check-exec-bits: self-check failed — zero candidate scripts found (broken scan?)" >&2
+    exit 2
+fi
+
 if [ "$findings" -eq 0 ]; then
-    echo "check-exec-bits: clean" >&2
+    echo "check-exec-bits: clean ($candidates scripts checked)" >&2
     exit 0
 fi
 

+ 5 - 1
tests/doc-drift.sh

@@ -49,12 +49,16 @@ fi
 # These five known count-bearing patterns are intentionally exhaustive; newly
 # introduced prose patterns must be added explicitly if they should be gated.
 check_readme_prose() { # $1=regex $2=disk-count $3=label
-    local line claim
+    local line claim matched=0
     while IFS= read -r line; do
         [ -n "$line" ] || continue
+        matched=$((matched + 1))
         claim="$(echo "$line" | grep -oE '[0-9]+' | head -1)"
         [ "$claim" = "$2" ] || err "README.md: $claim $3 claimed, $2 on disk"
     done < <(grep -E "$1" README.md || true)
+    # Zero matches = the count-bearing line was deleted or reworded, which is
+    # drift too — a guard that finds nothing to check must not stay silent.
+    [ "$matched" -ge 1 ] || err "README.md: prose pattern for '$3' matched no lines (deleted/reworded?)"
 }
 check_readme_prose '[0-9]+ specialized skills' "$skills_disk" "specialized skills"
 check_readme_prose '[0-9]+ on-demand skills' "$skills_disk" "on-demand skills"