fix(lint): restore release-gate coverage of bundled Vale assets

The gate derived release-relevant paths from the dirname of each
entry's --config target. Dropping --config from .pre-commit-hooks.yaml
left that loop dead, silently removing both assets/vale/ trees from
coverage — a Vale rule change could land on main without demanding a
release tag, leaving consumers pinned to an old rev: with stale rules.

Coverage now derives from tokens[0] instead: double-dirname for the ..
normalization, guarded on the tree existing and on the bundle root not
resolving to "." so skill-size-check.sh cannot invent a bogus path.

The --config branch is removed rather than kept as dead code. Since
pre-commit rewrites only entry[0], no argument in any entry can ever
name a file this repo ships, so that shape is broken by design.

Known gap: deleting a hook's entire assets/ tree is not flagged, as the
candidate path stops existing. Deletions within a surviving tree are.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MCQ648fLSFXPHGZdQ8gn58
This commit is contained in:
2026-08-09 13:06:37 +00:00
parent 714e8a0c78
commit 348dd9f665
2 changed files with 80 additions and 25 deletions

View File

@@ -34,19 +34,29 @@ fi
# instead of hand-maintaining a parallel list — the manifest is the single
# source of truth for what external consumers actually pull at a pinned rev,
# so a hook added/removed/renamed there can't silently drift out of sync here.
# Each entry is "<script> [--config <path>] [...]"; the script itself and the
# directory holding any --config target (vale-wrap.sh needs its .vale.ini's
# sibling styles/ tree, not just the ini file) are release-relevant.
# Everything is derived from tokens[0], the hook's script: pre-commit prefixes
# only entry[0] with the hook-repo clone path, so any later token that looks
# like a path resolves against the *consuming* repo and can never name a file
# this repo ships. A hook's bundled data therefore has to be self-located
# relative to the script — vale-wrap.sh reads its own
# <script-dir>/../assets/vale/.vale.ini plus the sibling styles/ tree — which
# makes <script-dir>/../assets release-relevant alongside the script itself.
# The ../ is normalised by stripping a path component rather than with
# `realpath -m`, which is a GNU-only extension. Two guards keep the derivation
# from inventing paths: a bundle root of "." is skipped, because a script in a
# top-level directory (scripts/skill-size-check.sh) would derive the repo's own
# shared assets/, which no hook owns and whose churn must not demand a release;
# and the directory is added only when it exists, since a hook that bundles
# nothing must not contribute a pathspec matching nothing.
RELEASE_PATHS=("$HOOKS_MANIFEST")
while IFS= read -r entry; do
read -ra tokens <<< "$entry"
[[ ${#tokens[@]} -eq 0 ]] && continue
RELEASE_PATHS+=("${tokens[0]}")
for ((i = 1; i < ${#tokens[@]}; i++)); do
if [[ "${tokens[i]}" == "--config" && -n "${tokens[i + 1]:-}" ]]; then
RELEASE_PATHS+=("$(dirname "${tokens[i + 1]}")")
fi
done
bundle_root="$(dirname "$(dirname "${tokens[0]}")")"
if [[ "$bundle_root" != "." && -d "$bundle_root/assets" ]]; then
RELEASE_PATHS+=("$bundle_root/assets")
fi
done < <(sed -n 's/^[[:space:]]*entry:[[:space:]]*//p' "$HOOKS_MANIFEST")
# Only vX.Y.Z release tags count as a baseline — an incidental checkpoint or