fix: purge AI slop from judgment essay, add voice linting pipeline
- Killed 16 em-dashes (Claude fingerprint, 15.9/1000 words -> 0) - Replaced all clause-dash-elaboration patterns with periods, colons, restructuring - Removed 'The One-Sentence Version' section (restated the intro, voice anti-pattern) - Broke tricolon at lines 56-58 (too-clean parallel structure) - Collapsed 'How to Actually Help' listicle into connective prose - Added receipt link for ~100x inference cost claim - Heading dashes replaced with colons (Tradesman Analogy, Excellence vs Functional) New tooling: - scripts/lint-voice.sh: mechanical anti-slop linter (em-dashes, trigger words, hedging, filler, receipts, sentence uniformity) - .amplifier/skills/voice-check/SKILL.md: LLM-as-judge voice authenticity check (8 dimensions against VOICE.md profile) - .amplifier/AGENTS.md: standing rule requiring both checks before any publish 🤖 Generated with Amplifier Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
This commit is contained in:
Executable
+162
@@ -0,0 +1,162 @@
|
||||
#!/usr/bin/env bash
|
||||
# lint-voice.sh -- Anti-slop linter for Crash Test Dev posts
|
||||
# Usage: ./scripts/lint-voice.sh src/content/posts/my-post.md
|
||||
#
|
||||
# Checks for AI writing tells and voice mismatches.
|
||||
# Exit code 0 = clean, 1 = issues found.
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
if [[ $# -lt 1 ]]; then
|
||||
echo "Usage: $0 <markdown-file>"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
FILE="$1"
|
||||
if [[ ! -f "$FILE" ]]; then
|
||||
echo "File not found: $FILE"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# Strip frontmatter (everything between first and second ---)
|
||||
BODY=$(sed -n '/^---$/,/^---$/!p' "$FILE" | tail -n +1)
|
||||
WORD_COUNT=$(echo "$BODY" | wc -w)
|
||||
ISSUES=0
|
||||
WARNINGS=0
|
||||
|
||||
echo "=== Voice Lint: $(basename "$FILE") ==="
|
||||
echo " Words: $WORD_COUNT"
|
||||
echo ""
|
||||
|
||||
# --- CHECK 1: Em-dash density ---
|
||||
EMDASH_COUNT=$(echo "$BODY" | grep -o '—' | wc -l)
|
||||
EMDASH_DOUBLE=$(echo "$BODY" | grep -oP '(?<!\-)--(?!\-)' | wc -l)
|
||||
TOTAL_DASHES=$((EMDASH_COUNT + EMDASH_DOUBLE))
|
||||
if [[ $WORD_COUNT -gt 0 ]]; then
|
||||
DENSITY=$(echo "scale=1; $TOTAL_DASHES * 1000 / $WORD_COUNT" | bc 2>/dev/null || echo "0")
|
||||
else
|
||||
DENSITY="0"
|
||||
fi
|
||||
|
||||
if [[ $TOTAL_DASHES -gt 5 ]]; then
|
||||
echo "FAIL Em-dashes: $TOTAL_DASHES found ($DENSITY per 1000 words)"
|
||||
echo " Normal: 1-3 per 1000 words. Yours: $DENSITY"
|
||||
echo " Lines:"
|
||||
grep -n '—\|[^-]--[^-]' "$FILE" | head -20 | sed 's/^/ /'
|
||||
echo ""
|
||||
ISSUES=$((ISSUES + 1))
|
||||
elif [[ $TOTAL_DASHES -gt 3 ]]; then
|
||||
echo "WARN Em-dashes: $TOTAL_DASHES found ($DENSITY per 1000 words)"
|
||||
echo " Getting close to AI territory. Consider replacing 1-2."
|
||||
echo ""
|
||||
WARNINGS=$((WARNINGS + 1))
|
||||
else
|
||||
echo "OK Em-dashes: $TOTAL_DASHES ($DENSITY per 1000 words)"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# --- CHECK 2: AI trigger words ---
|
||||
TRIGGER_WORDS="delve|tapestry|multifaceted|nuanced|landscape|comprehensive|pivotal|leverage[sd]?|leveraging|robust|utilize[sd]?|utilizing|seamless|cutting-edge|game.changer|paradigm|synergy|holistic|streamline"
|
||||
TRIGGERS=$(echo "$BODY" | grep -oiP "\b($TRIGGER_WORDS)\b" | sort | uniq -c | sort -rn)
|
||||
if [[ -n "$TRIGGERS" ]]; then
|
||||
echo "FAIL AI trigger words found:"
|
||||
echo "$TRIGGERS" | sed 's/^/ /'
|
||||
echo ""
|
||||
ISSUES=$((ISSUES + 1))
|
||||
else
|
||||
echo "OK No AI trigger words"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# --- CHECK 3: Hedging phrases ---
|
||||
HEDGE_PATTERNS="it.s worth noting|it is worth noting|it.s important to note|one might argue|it could be said|in today.s digital|in today.s rapidly|in the realm of|it goes without saying|at the end of the day|in an era where|it is important to understand"
|
||||
HEDGES=$(echo "$BODY" | grep -oiP "$HEDGE_PATTERNS" | sort | uniq -c | sort -rn)
|
||||
if [[ -n "$HEDGES" ]]; then
|
||||
echo "FAIL Hedging phrases found:"
|
||||
echo "$HEDGES" | sed 's/^/ /'
|
||||
echo ""
|
||||
ISSUES=$((ISSUES + 1))
|
||||
else
|
||||
echo "OK No hedging phrases"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# --- CHECK 4: Filler transitions ---
|
||||
FILLER_PATTERNS="in conclusion|moving on|with that said|that being said|to summarize|in summary|as we.ve seen|as mentioned earlier|without further ado|let.s dive in|let.s delve"
|
||||
FILLERS=$(echo "$BODY" | grep -oiP "$FILLER_PATTERNS" | sort | uniq -c | sort -rn)
|
||||
if [[ -n "$FILLERS" ]]; then
|
||||
echo "FAIL Filler transitions found:"
|
||||
echo "$FILLERS" | sed 's/^/ /'
|
||||
echo ""
|
||||
ISSUES=$((ISSUES + 1))
|
||||
else
|
||||
echo "OK No filler transitions"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# --- CHECK 5: Resolution closers ---
|
||||
CLOSER_PATTERNS="the key takeaway|the takeaway here|at the end of the day|what this means is|the bottom line|the real question is"
|
||||
CLOSERS=$(echo "$BODY" | grep -oiP "$CLOSER_PATTERNS" | sort | uniq -c | sort -rn)
|
||||
if [[ -n "$CLOSERS" ]]; then
|
||||
echo "WARN Resolution closer phrases found:"
|
||||
echo "$CLOSERS" | sed 's/^/ /'
|
||||
echo ""
|
||||
WARNINGS=$((WARNINGS + 1))
|
||||
else
|
||||
echo "OK No resolution closers"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# --- CHECK 6: Receipts check (links and code blocks) ---
|
||||
LINK_COUNT=$(echo "$BODY" | grep -oP '\[.*?\]\(http[^)]+\)' | wc -l)
|
||||
URL_COUNT=$(echo "$BODY" | grep -oP 'https?://[^\s\)]+' | wc -l)
|
||||
CODE_BLOCK_COUNT=$(echo "$BODY" | grep -c '^```' || true)
|
||||
CODE_BLOCK_COUNT=$((CODE_BLOCK_COUNT / 2))
|
||||
|
||||
TOTAL_EVIDENCE=$((LINK_COUNT + URL_COUNT + CODE_BLOCK_COUNT))
|
||||
if [[ $TOTAL_EVIDENCE -eq 0 && $WORD_COUNT -gt 500 ]]; then
|
||||
echo "WARN No receipts: 0 links, 0 URLs, 0 code blocks in $WORD_COUNT words"
|
||||
echo " Ken's voice rule: \"Show receipts. Always. Non-negotiable.\""
|
||||
echo " Consider: Is this a manifesto (OK without receipts) or a war story (needs them)?"
|
||||
echo ""
|
||||
WARNINGS=$((WARNINGS + 1))
|
||||
else
|
||||
echo "OK Evidence: $LINK_COUNT links, $URL_COUNT URLs, $CODE_BLOCK_COUNT code blocks"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# --- CHECK 7: Sentence length variation ---
|
||||
# Count sentences (rough: split on . ! ? followed by space or newline)
|
||||
SENTENCES=$(echo "$BODY" | grep -oP '[^.!?]+[.!?]' | wc -l)
|
||||
if [[ $SENTENCES -gt 5 ]]; then
|
||||
# Get word counts per sentence
|
||||
LENGTHS=$(echo "$BODY" | grep -oP '[^.!?]+[.!?]' | while read -r sent; do echo "$sent" | wc -w; done)
|
||||
AVG=$(echo "$LENGTHS" | awk '{sum+=$1} END {printf "%.0f", sum/NR}')
|
||||
# Count sentences within 5 words of average
|
||||
UNIFORM=$(echo "$LENGTHS" | awk -v avg="$AVG" '{if ($1 >= avg-5 && $1 <= avg+5) count++} END {printf "%.0f", (count/NR)*100}')
|
||||
if [[ $UNIFORM -gt 70 ]]; then
|
||||
echo "WARN Sentence uniformity: ${UNIFORM}% within 5 words of average ($AVG words)"
|
||||
echo " AI benchmark: >70% uniform. Human writing varies wildly."
|
||||
echo " Try: Add some 5-word punches. Break up medium sentences."
|
||||
echo ""
|
||||
WARNINGS=$((WARNINGS + 1))
|
||||
else
|
||||
echo "OK Sentence variation: ${UNIFORM}% uniform (avg $AVG words/sentence)"
|
||||
echo ""
|
||||
fi
|
||||
fi
|
||||
|
||||
# --- SUMMARY ---
|
||||
echo "=== Summary ==="
|
||||
if [[ $ISSUES -eq 0 && $WARNINGS -eq 0 ]]; then
|
||||
echo "CLEAN -- No issues found"
|
||||
exit 0
|
||||
elif [[ $ISSUES -eq 0 ]]; then
|
||||
echo "$WARNINGS warning(s), 0 failures"
|
||||
echo "Review warnings but publishable."
|
||||
exit 0
|
||||
else
|
||||
echo "$ISSUES FAILURE(s), $WARNINGS warning(s)"
|
||||
echo "Fix failures before publishing."
|
||||
exit 1
|
||||
fi
|
||||
Reference in New Issue
Block a user