fix(doctor): kill 3 permanent false sentinels (LRN-047 class)
- EXPECTED_DENY hardcoded 100 vs 99 real → derive from committed settings.json (HEAD): auto-tracks legit deny edits, still flags live-vs-committed divergence. - EXPECTED_SKILLS required gstack 'health' (OFF by default, profile- managed): false warn on a default install with a wrong remedy — link.sh cannot restore gstack skills. Dropped; 'status' kept (repo- owned personal skill, git ls-files proven). - disable-model-invocation check required a key BDR-019 stripped repo-wide (2026-06-09) → warned on every owned skill since. Inverted into a BDR-019 regression watch. - pass message derives the skill list from the array (LRN-005 class: no hardcoded display drift). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016zA3Qh2Q1QpcGXzXxKeDHR
This commit is contained in:
co-authored by
Claude Fable 5
parent
17fb6dda43
commit
ca8df16885
@@ -213,11 +213,20 @@ print(len(d.get('permissions',{}).get('deny',[])))
|
|||||||
if [ "$DENY_COUNT" = "?" ]; then
|
if [ "$DENY_COUNT" = "?" ]; then
|
||||||
warn "Could not parse deny count (python3 unavailable or JSON parse error)"
|
warn "Could not parse deny count (python3 unavailable or JSON parse error)"
|
||||||
else
|
else
|
||||||
EXPECTED_DENY=100
|
# Expected = deny count in the last COMMITTED settings.json. A hardcoded
|
||||||
if [ "$DENY_COUNT" -eq "$EXPECTED_DENY" ] 2>/dev/null; then
|
# number drifts on every legit deny-list edit (false-warned for weeks at
|
||||||
pass "Deny rules: $DENY_COUNT"
|
# 100 vs 99 — LRN-047 class); deriving from HEAD auto-tracks legit edits
|
||||||
|
# and still flags live-vs-committed divergence.
|
||||||
|
EXPECTED_DENY=$(git -C "$REPO" show HEAD:settings.json 2>/dev/null | python3 -c "
|
||||||
|
import json,sys
|
||||||
|
print(len(json.load(sys.stdin).get('permissions',{}).get('deny',[])))
|
||||||
|
" 2>/dev/null || echo "?")
|
||||||
|
if [ "$EXPECTED_DENY" = "?" ]; then
|
||||||
|
warn "Could not derive expected deny count from committed settings.json"
|
||||||
|
elif [ "$DENY_COUNT" -eq "$EXPECTED_DENY" ] 2>/dev/null; then
|
||||||
|
pass "Deny rules: $DENY_COUNT (matches committed settings.json)"
|
||||||
else
|
else
|
||||||
warn "Deny rules: $DENY_COUNT (expected $EXPECTED_DENY) — settings may have been manually modified"
|
warn "Deny rules: $DENY_COUNT (committed: $EXPECTED_DENY) — live settings diverge from last commit"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
@@ -310,8 +319,11 @@ else
|
|||||||
warn "gstack/browse/dist/ symlink missing — run: bash link.sh"
|
warn "gstack/browse/dist/ symlink missing — run: bash link.sh"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check owned skills have disable-model-invocation (skip external/symlinked skills)
|
# BDR-019 (2026-06-09) stripped disable-model-invocation repo-wide so the
|
||||||
MISSING_DMI=()
|
# model/orchestrators can self-route. The old check required the key on
|
||||||
|
# every owned skill — permanent false-warn since. Inverted: warn if any
|
||||||
|
# owned skill REintroduces the key (regression watch on BDR-019).
|
||||||
|
PRESENT_DMI=()
|
||||||
for f in "$HOME/.claude/skills/"*/SKILL.md; do
|
for f in "$HOME/.claude/skills/"*/SKILL.md; do
|
||||||
[ -f "$f" ] || continue
|
[ -f "$f" ] || continue
|
||||||
dir=$(dirname "$f")
|
dir=$(dirname "$f")
|
||||||
@@ -319,19 +331,22 @@ for f in "$HOME/.claude/skills/"*/SKILL.md; do
|
|||||||
[ -L "$dir" ] && continue
|
[ -L "$dir" ] && continue
|
||||||
[ -L "$f" ] && continue
|
[ -L "$f" ] && continue
|
||||||
name=$(basename "$dir")
|
name=$(basename "$dir")
|
||||||
if ! grep -q "disable-model-invocation" "$f" 2>/dev/null; then
|
if grep -q "disable-model-invocation" "$f" 2>/dev/null; then
|
||||||
MISSING_DMI+=("$name")
|
PRESENT_DMI+=("$name")
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
if [ ${#MISSING_DMI[@]} -eq 0 ]; then
|
if [ ${#PRESENT_DMI[@]} -eq 0 ]; then
|
||||||
pass "All owned skills have disable-model-invocation"
|
pass "No owned skill carries disable-model-invocation (BDR-019)"
|
||||||
else
|
else
|
||||||
warn "Owned skills missing disable-model-invocation: ${MISSING_DMI[*]}"
|
warn "Owned skills reintroduce disable-model-invocation (BDR-019 regression): ${PRESENT_DMI[*]}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check expected skills are present
|
# Check expected skills are present. Repo-owned skills only: gstack skills
|
||||||
|
# (health, status, …) are OFF by default and toggled per profile — requiring
|
||||||
|
# them here false-warns on a default install, and "run link.sh" cannot
|
||||||
|
# restore them (they are profile-managed, not link.sh-managed).
|
||||||
EXPECTED_SKILLS=(
|
EXPECTED_SKILLS=(
|
||||||
"analyze" "doc" "health" "init-project" "onboard" "plugin-check"
|
"analyze" "doc" "init-project" "onboard" "plugin-check"
|
||||||
"refactor" "ship-feature" "status"
|
"refactor" "ship-feature" "status"
|
||||||
)
|
)
|
||||||
MISSING_SKILLS=()
|
MISSING_SKILLS=()
|
||||||
@@ -341,7 +356,7 @@ for skill in "${EXPECTED_SKILLS[@]}"; do
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
if [ ${#MISSING_SKILLS[@]} -eq 0 ]; then
|
if [ ${#MISSING_SKILLS[@]} -eq 0 ]; then
|
||||||
pass "All ${#EXPECTED_SKILLS[@]} expected skills present (analyze, doc, health, init-project, onboard, plugin-check, refactor, ship-feature, status)"
|
pass "All ${#EXPECTED_SKILLS[@]} expected skills present (${EXPECTED_SKILLS[*]})"
|
||||||
else
|
else
|
||||||
warn "Missing skills: ${MISSING_SKILLS[*]} — run: bash link.sh"
|
warn "Missing skills: ${MISSING_SKILLS[*]} — run: bash link.sh"
|
||||||
fi
|
fi
|
||||||
|
|||||||
Reference in New Issue
Block a user