Skip to content

validate-content.sh aborts (exit 128, no Summary) outside a tagged git checkout — missing || echo "" guard on git-tag call (v2.18.3) #233

@pitimon

Description

@pitimon

Summary

tests/validate-content.sh aborts with exit 128 and prints no === Summary === block when run outside a tagged git checkout (e.g. from the installed plugin cache, a shallow CI clone without fetch-tags, or a fork before its first v2.* tag). The companion tests/validate-structure.sh handles the same no-git situation gracefully, so this is an inconsistency in one of the two shipped self-test validators.

Discovered during a QA pass on the v2.18.3 release.

Root cause

tests/validate-content.sh:654:

all_tags=$(git tag -l "v2.*" 2>/dev/null | sort -V)

The script runs under set -euo pipefail. In a non-git directory git tag -l exits 128; 2>/dev/null suppresses the stderr but not the exit code, and pipefail propagates 128 out of the pipeline. The command substitution then carries that status into the assignment, and errexit aborts the script immediately — before the intended fallback at lines 655–656 can run:

all_tags=$(git tag -l "v2.*" 2>/dev/null | sort -V)
if [ -z "$all_tags" ]; then
  warn "git tag history unavailable — skipping SELF-CHECK.md body freshness ..."

The inline comment at line 653 documents the intended behavior the code doesn't deliver:

# Dev-env: no tags → WARN+skip (CI sets fetch-tags: true so drift still caught at merge).

The sibling tests/validate-structure.sh already guards every git call correctly, e.g.:

# validate-structure.sh:797 / :803
LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
CHANGED=$(git diff --name-only "$DIFF_BASE"..HEAD 2>/dev/null || echo "")

validate-content.sh:654 is the only git call missing that || echo "" guard.

Reproduce

$ cp -R <installed 2.18.3 tree> /tmp/nb283   # any non-git dir
$ cd /tmp/nb283 && bash tests/validate-content.sh; echo "exit=$?"
...
  PASS: docs/wiki/Changelog.md badge matches v2.18.3
exit=128            # <-- no "=== Summary ===", no RESULT line

Isolated mechanism:

$ bash -c 'set -euo pipefail; x=$(git tag -l "v2.*" 2>/dev/null | sort -V); echo "REACHED"'; echo $?
128                 # "REACHED" never prints — aborts at the assignment

tests/validate-structure.sh in the same directory completes normally (358 PASS / 0 FAIL, Check 27 cleanly reports "no release tag yet — Check 27 skipped").

Impact

  • The release notes' "269 PASS / 0 FAIL / 1 WARN (content)" is only reproducible inside the maintainer's tagged git checkout. The shipped artifact, run standalone, yields exit 128 with no summary — which reads as a crash, not a clean skip.
  • Affects any consumer running the shipped self-test from the installed plugin, plus shallow CI clones without fetch-tags: true and forks before their first v2.* tag.
  • H1 (Be Proactive) lens: for a plugin whose thesis is verification discipline, one of its two verification surfaces isn't proactive about the no-git case the other one already handles.

Proposed fix (illustration — not a PR)

One line, matching the guard validate-structure.sh already uses:

--- a/tests/validate-content.sh
+++ b/tests/validate-content.sh
@@ -651,7 +651,7 @@
-  all_tags=$(git tag -l "v2.*" 2>/dev/null | sort -V)
+  all_tags=$(git tag -l "v2.*" 2>/dev/null | sort -V || echo "")

Verified: with this guard, the same non-git run completes exit=0
=== Summary === PASS: 267 / FAIL: 0 / WARN: 2 / ALL CHECKS PASSED
(the 2nd WARN is the intended "git tag history unavailable — skipping sub-checks E + F"). Inside a tagged checkout, behavior is unchanged: E+F run and the figure is the documented 269 PASS / 1 WARN.

Secondary note (LOW / by-design question)

The new guides/anthropic-engineering-doctrine-audit.md Table 1 cites 3 maintainer-local ~/.claude/lessons/... paths (the auto-commit-hook lesson, the notebook-agent-harness lesson, and INDEX.md) that aren't shipped with the plugin, so 3 of the 12 "verify before re-proposing" rows can't be checked by a consumer. The guide self-describes as a contributor/maintainer defensive-citation surface, so this may be intentional — flagging in case a one-line "lesson citations are maintainer-local" disclaimer (or relativized paths) would help consumers reading Table 1.


QA context: v2.18.3, commit f0fa240. Structure validator and all release-notes content/version claims otherwise verified correct.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions