fix(claude-design): derive shipped-content scope from git, not a name list
Checks (i) and (j) decided what counts as "shipped content" from a hardcoded
list of local-only file names (REMEMBER.md, TODO.md, NEXT-SESSION-PROMPT.local.md).
That list was written before the STATE.md convention replaced those three, so
STATE.md fell through it and was scanned as if it shipped. It does not: it is
gitignored and has zero tracked entries.
The resulting false positive was self-reproducing. Any STATE.md note explaining
why the check was red had to name the banned token, which made the check red.
Removing the offending line closed nothing; the next session that documented
the finding recreated it.
Two fixes were considered:
(a) add STATE.md to the exclude list. One name, but the list stays a name
list -- it rots again the next time a local-only file is renamed, which
is precisely how this defect arrived.
(b) derive the scope from git. No gitignored file can reopen the hole,
whatever it is called.
(b) is implemented, via `git check-ignore` rather than `git ls-files`. Both
answer "is this shipped", but ls-files also drops untracked Markdown that is
NOT ignored -- new content on its way into the plugin, which is exactly when a
leak check should be looking. check-ignore keeps that in scope and excludes
only what git ignores. When git cannot answer (no repo, no binary) every file
is treated as shipped, so the checks fail loudly instead of passing on an
empty file list.
Verified both directions, denominators reported:
known-positive: a real shipped reference/*.md carrying the banned token
-> FAIL, exit 1 (proves the check can still fire)
known-negative: STATE.md, gitignored, carrying the same token
-> not flagged
check (j) known-positive: shipped .md with Norwegian diacritics -> WARN
scope: 22 shipped Markdown files of 31 on disk (9 gitignored: STATE.md +
8 under .claude/)
validate-plugin.sh: Pass 16 / Fail 0 / Warn 0, exit 0 (was 14 / 1 / 23, exit 1)
verify.sh roll-up: Pass 41 / Fail 0 / Warn 1, exit 0
Both re-run under /bin/bash 3.2.57 as well as bash 5.3.
The 23 warnings that disappeared were all STATE.md diacritics; they were never
shipped content, and check (j) carried the same name-list defect that (i) did.
Closes ORDRE 64.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ToLVakwASPe3pXsdEiothC
This commit is contained in:
parent
046f022436
commit
3696b8e283
1 changed files with 64 additions and 17 deletions
|
|
@ -206,20 +206,72 @@ if [ "$H_HIT" -eq 0 ]; then
|
|||
fi
|
||||
echo ""
|
||||
|
||||
# -------------------------------------------------------
|
||||
# Shipped-content enumeration (shared by checks (i) and (j))
|
||||
#
|
||||
# "Shipped" is derived from git, not from a list of file names: any file git
|
||||
# ignores is session state that never leaves this machine, so it is not shipped
|
||||
# content. A name list cannot hold this line -- it already rotted once, when the
|
||||
# STATE.md convention replaced the three local-only files it knows about, and
|
||||
# the resulting false positive was self-reproducing (any note explaining the
|
||||
# failure re-triggered it).
|
||||
#
|
||||
# Derived from `git check-ignore` rather than `git ls-files` on purpose: an
|
||||
# untracked Markdown file that is NOT ignored is about to ship and must stay in
|
||||
# scope, so new content is checked before it is ever staged.
|
||||
#
|
||||
# When git cannot answer (no repo, no git binary) every file is treated as
|
||||
# shipped: conservative, so these checks can still fail rather than silently
|
||||
# passing on an empty file list.
|
||||
# -------------------------------------------------------
|
||||
echo "--- shipped-content enumeration (git-derived) ---"
|
||||
|
||||
GIT_AVAILABLE=1
|
||||
if ! git -C "$PLUGIN_ROOT" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
||||
GIT_AVAILABLE=0
|
||||
fi
|
||||
|
||||
shipped_md_files() {
|
||||
find "$PLUGIN_ROOT" -type f -name '*.md' "$@" 2>/dev/null | sort | while IFS= read -r f; do
|
||||
if [ "$GIT_AVAILABLE" -eq 1 ] && git -C "$PLUGIN_ROOT" check-ignore -q -- "$f" 2>/dev/null; then
|
||||
continue
|
||||
fi
|
||||
printf '%s\n' "$f"
|
||||
done
|
||||
}
|
||||
|
||||
# grep_shipped <ERE> [find-predicates...] -> "path:line:text" lines
|
||||
grep_shipped() {
|
||||
local regex="$1"
|
||||
shift
|
||||
local f hits line
|
||||
shipped_md_files "$@" | while IFS= read -r f; do
|
||||
[ -n "$f" ] || continue
|
||||
hits="$(grep -nE "$regex" "$f" 2>/dev/null || true)"
|
||||
[ -n "$hits" ] || continue
|
||||
printf '%s\n' "$hits" | while IFS= read -r line; do
|
||||
printf '%s:%s\n' "$f" "$line"
|
||||
done
|
||||
done
|
||||
}
|
||||
|
||||
if [ "$GIT_AVAILABLE" -eq 0 ]; then
|
||||
warn "git unavailable: checks (i) and (j) treat every Markdown file as shipped content"
|
||||
fi
|
||||
printf " shipped Markdown files in scope: %s (of %s on disk)\n" \
|
||||
"$(shipped_md_files | wc -l | tr -d ' ')" \
|
||||
"$(find "$PLUGIN_ROOT" -type f -name '*.md' 2>/dev/null | wc -l | tr -d ' ')"
|
||||
echo ""
|
||||
|
||||
# -------------------------------------------------------
|
||||
# Check (i): operator-private-context grep
|
||||
# -------------------------------------------------------
|
||||
echo "--- (i) operator-private-context grep ---"
|
||||
|
||||
I_HITS="$(grep -rnE '(kjell|vegvesen|NEXT-SESSION-PROMPT|REMEMBER\.md content from)' \
|
||||
"$PLUGIN_ROOT" \
|
||||
--include='*.md' \
|
||||
--exclude-dir='.claude' \
|
||||
--exclude-dir='tests' \
|
||||
--exclude='REMEMBER.md' \
|
||||
--exclude='TODO.md' \
|
||||
--exclude='NEXT-SESSION-PROMPT.local.md' \
|
||||
2>/dev/null || true)"
|
||||
I_HITS="$(grep_shipped '(kjell|vegvesen|NEXT-SESSION-PROMPT|REMEMBER\.md content from)' \
|
||||
-not -path "$PLUGIN_ROOT/.claude/*" \
|
||||
-not -path "$PLUGIN_ROOT/tests/*" \
|
||||
|| true)"
|
||||
|
||||
if [ -z "$I_HITS" ]; then
|
||||
pass "no operator-private context leaks in shipped content"
|
||||
|
|
@ -235,14 +287,9 @@ echo ""
|
|||
# -------------------------------------------------------
|
||||
echo "--- (j) Norwegian-leakage grep ---"
|
||||
|
||||
J_HITS="$(grep -rnE '[æøåÆØÅ]' \
|
||||
"$PLUGIN_ROOT" \
|
||||
--include='*.md' \
|
||||
--exclude-dir='.claude' \
|
||||
--exclude='REMEMBER.md' \
|
||||
--exclude='TODO.md' \
|
||||
--exclude='NEXT-SESSION-PROMPT.local.md' \
|
||||
2>/dev/null || true)"
|
||||
J_HITS="$(grep_shipped '[æøåÆØÅ]' \
|
||||
-not -path "$PLUGIN_ROOT/.claude/*" \
|
||||
|| true)"
|
||||
|
||||
if [ -z "$J_HITS" ]; then
|
||||
pass "no Norwegian diacritics in shipped content"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue