From ebe119a2b7a1f8e631b082821d99ea7c9ab25039 Mon Sep 17 00:00:00 2001 From: Will Curran Date: Fri, 31 Jul 2026 16:23:50 -0700 Subject: [PATCH] docs: lint the same files CI does, then make CI keep them level CONTRIBUTING told contributors to run shellcheck locally "because it runs in CI, so running it here saves a round trip", then listed five files where CI lints six. tests/check-links.sh was missing. A contributor could follow the documented steps exactly, see a silent shellcheck, push, and fail the lint job on a file they were never told to check. A doc that is wrong about how to pass CI is worse than no doc, because it is trusted. Correcting the list alone would leave the two free to drift apart again, which is the same shape as the false install-path claim that was fixed in README and left standing in ROADMAP for weeks. So both lists are now read from the file that owns them and compared as sets, and neither can be updated alone. A third assertion checks that every listed file exists. Two lists that agree with each other but no longer match the repo is the failure a set comparison structurally cannot catch, so it needs its own check. Co-Authored-By: Claude Opus 5 --- CONTRIBUTING.md | 2 +- tests/run.sh | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8a2faa0..62fcddc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -58,7 +58,7 @@ Both of these run in CI, so running them locally saves a round trip: ```bash bash tests/run.sh -shellcheck bin/claude-team bin/team-session-start install.sh scripts/generate-agents.sh tests/run.sh +shellcheck bin/claude-team bin/team-session-start install.sh scripts/generate-agents.sh tests/run.sh tests/check-links.sh ``` The suite must be fully green. Shellcheck must be silent; it reads `.shellcheckrc` from the repo root, which documents the three disables and why each exists. diff --git a/tests/run.sh b/tests/run.sh index 757077f..1ff9e89 100644 --- a/tests/run.sh +++ b/tests/run.sh @@ -1652,6 +1652,43 @@ echo "" # commit that fixed a real drift) before being written, not against invented # examples. +echo "CONTRIBUTING shellcheck command <-> ci.yml" + +# CONTRIBUTING tells a contributor to run shellcheck locally "because it runs in +# CI, so running it here saves a round trip". That promise only holds while the +# two lists name the same files. CONTRIBUTING listed five and CI ran six, missing +# tests/check-links.sh, so a contributor could follow the documented steps, see a +# silent shellcheck, push, and fail the lint job on a file they were never told +# to check. A doc that is wrong about how to pass CI is worse than no doc. +# +# Both sides are read from the file that owns them and compared as sets, so +# neither can be updated alone. +sc_from() { + grep -oE 'shellcheck( +[A-Za-z0-9_./-]+)+' "$1" | head -1 | tr ' ' '\n' | sed 1d | sort +} +ci_sc=$(sc_from "$REPO_DIR/.github/workflows/ci.yml") +doc_sc=$(sc_from "$REPO_DIR/CONTRIBUTING.md") +if [[ -n "$ci_sc" && -n "$doc_sc" ]]; then + ok "found a shellcheck file list on both sides, so this test is not vacuous" +else + fail "found a shellcheck file list on both sides, so this test is not vacuous (ci:$(wc -l <<< "$ci_sc") doc:$(wc -l <<< "$doc_sc"))" +fi +if [[ "$ci_sc" == "$doc_sc" ]]; then + ok "CONTRIBUTING lints the same files CI does" +else + fail "CONTRIBUTING lints the same files CI does (only in CI:$(comm -23 <(echo "$ci_sc") <(echo "$doc_sc") | tr '\n' ' ')| only in CONTRIBUTING:$(comm -13 <(echo "$ci_sc") <(echo "$doc_sc") | tr '\n' ' '))" +fi +# Both lists staying in step with each other but drifting from the repo is the +# failure a set comparison structurally cannot catch, so name the files too. +missing_sc="" +while read -r f; do + [[ -z "$f" ]] && continue + [[ -f "$REPO_DIR/$f" ]] || missing_sc="$missing_sc $f" +done <<< "$ci_sc" +if [[ -z "$missing_sc" ]]; then ok "every file in the shellcheck list exists" +else fail "every file in the shellcheck list exists (missing:$missing_sc)"; fi +echo "" + echo "commands/ <-> README shipped-commands" # Every persona command (profiles/.md, generated into