fix(scripts): decide .vale.ini readability by reading it, not by access(2)

Issue #97 item 1 reports the unreadable-.vale.ini guard as untested. It
was worse: it was dead. `[[ -r ]]` is access(2), which asks whether the
permission bits would allow a read -- and for uid 0 that is yes even on
a mode-000 file. This hook runs at pre-push and the dev environment is
root, so the guard could never fire where it exists to fire. That is
why no uid-independent test for it existed; there was nothing to test.

Readability is now decided by actually reading (`cat`), which is
uid-independent and strictly stronger, catching EISDIR and EIO that
access(2) reports on neither. `cat`, not a `< "$ini"` redirect: opening
a directory for reading succeeds, only the read fails. The missing
branch moves to `-e`, so a directory sitting where the file belongs is
reported as unreadable rather than sending the reader hunting for a
deleted file.

The new case asserts the MESSAGE, not the exit code. With the guard
removed the script still exits 1 -- the greps hit the unreadable path
and blame a missing StylesPath on a file that has one. An exit-code-only
test would have been green with the guard deleted.

Also stops paying for vale in cases that only assert .vale.ini text:
21 of 28 script runs now mask it via the PATH_NO_VALE mechanism case 12
already builds, cutting the suite's bottleneck ~3.5x (issue #97 item 5).
The helper falls back to an unmasked run rather than skipping, so a
machine where masking is unavailable loses speed, never coverage.

That masking is a coverage gain, not only a speedup. With vale on PATH,
cases 8 and 9 could not detect deletion of the assertions they were
written to catch: a dropped StylesPath also breaks the glob probe, so
the script exited 1 for the wrong reason and both cases went green.
Verified against the pre-change files -- the same mutation was caught by
one incidental assertion before, and by three after.

Refs #97

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X7GvKuJfy2WrdBmUttV4DT
This commit is contained in:
2026-08-14 08:03:58 +00:00
parent 5a61b417c9
commit c442f7eb85
2 changed files with 104 additions and 10 deletions

View File

@@ -67,15 +67,31 @@ AGENT_INI="$AGENT_AUDIT/assets/vale/.vale.ini"
for ini in "$SKILL_INI" "$AGENT_INI"; do
rel_ini="${ini#"$REPO_ROOT"/}"
if [[ ! -f "$ini" ]]; then
# `-e`, not `-f`: a path that exists but is not a readable regular file (a
# directory sitting where the file should be, say) is not "missing", and
# reporting it as missing sends you looking for a deleted file. It belongs to
# the unreadable case below, which is the one that describes what actually
# went wrong.
if [[ ! -e "$ini" ]]; then
err "$rel_ini is missing — without it vale falls back to an upward config search and lints with whatever it finds"
continue
fi
# Present but unreadable is its own case: every assertion below is a grep, and
# grep exits 2 on a read error. The override capture swallows that into an
# empty result, which would read as "no findings" rather than "not checked".
if [[ ! -r "$ini" ]]; then
err "$rel_ini is not readable — none of its assertions could run, and an unreadable file cannot be distinguished from a clean one downstream"
# empty result, which would read as "no findings" rather than "not checked",
# and the two greps above it report "has no StylesPath"/"names no Kyberforge"
# for a file that may well have both — a misdiagnosis, not a missed one.
#
# Decided by ACTUALLY READING the file, not by `[[ -r ]]`. `-r` is access(2),
# which answers "would the permission bits allow it" — and for uid 0 that is
# yes even on a mode-000 file (verified). This hook runs at pre-push, and this
# repo's dev environment is root, so an `[[ ! -r ]]` guard could never fire in
# the one place it exists to fire: it was untestable because it was dead. A
# read attempt is also the stricter question, catching EISDIR and EIO, which
# access(2) reports on neither. `cat`, not a bare `< "$ini"` redirect: opening
# a directory for reading succeeds, only the read fails.
if ! cat "$ini" >/dev/null 2>&1; then
err "$rel_ini exists but could not be read — none of its assertions could run, and an unreadable file cannot be distinguished from a clean one downstream"
continue
fi
# StylesPath is resolved relative to the .vale.ini, which is the only reason