/ devlog / devscripts / check_words.sh
#!/bin/sh
# Reads a draft and names every banned word and every sentence that
# describes how it is being said rather than what was found.
#
#   check_words.sh draft.txt      check a draft, and stamp that it was
#                                 checked this round
#   check_words.sh --test         run the self-test over the matcher
#
# Two kinds of fault are looked for.
#
# The first is a fixed list: words Chris has asked never to appear.
#
# The second is a shape, which is where the failures keep happening.
# Every banned lead-in occupies the same slot: a clause whose subject is
# the writer and whose verb is about speaking, placed in front of a
# fact. "Let me be plain", "I will say so", "I should note" are all that
# shape. The cure is not a longer list but a rule about sentence
# openers, since that is where they live: no sentence may open with a
# clause about how it is being said. Write the fact with the thing as
# its subject instead.
#
# WHY THE STAMP. The list has never been the weak part: the word that
# reached Chris last was already on it. What failed was that the check
# ran over a patch and never over the response. So a run against a draft
# leaves a stamp naming what Chris said this round, and cut_patch.sh
# refuses to cut without a stamp for the round it is in.
#
# A patch is read as the lines it adds. A line a patch takes away
# carries the old wording, and taking a banned word out of the codebase
# must not be reported as putting one in.
#
# Exits non-zero when anything is found, so it can gate a send.
WORDS='harness|ctor|rails|sidecar|fixture|nit|goldens|slug|seamless'
WORDS="$WORDS|proof|proven|honest|honestly|honesty|dishonest|genuinely"
WORDS="$WORDS|plainly|frankly|candidly|candid|candor|truthfully"
WORDS="$WORDS|sincerely|transparently|openly"
# "actually" says what came before it was not so, which is the same
# implication as the rest of this list, and the process file names it
# among them. It went out three times with the check quiet on it.
WORDS="$WORDS|actually"
# A bare "let" matched a JavaScript declaration, so a patch carrying
# "let menu = button.nextElementSibling;" was refused. Only the forms
# naming a speaker are the shape.
SHAPES='let me|let us|let'"'"'s say|i will say|i will be|i should say'
SHAPES="$SHAPES|i should note|i want to be|i have to say|i must say"
SHAPES="$SHAPES|to be (honest|fair|clear|blunt|direct|frank)"
SHAPES="$SHAPES|in (all )?(fairness|honesty)"
SHAPES="$SHAPES|for what it""'""s worth"
SHAPES="$SHAPES|the (honest|plain|simple) (truth|answer|finding)"
SHAPES="$SHAPES|my (honest|candid) (read|view)|no dressing|dress it up"
SHAPES="$SHAPES|straight with you|be straight|being straight"
SHAPES="$SHAPES|the (truth|reality) is|in truth|to be sure"
STAMP=${STAMP:-/home/claude/words_checked.txt}
SAID=${SAID:-/home/claude/said_this_round.txt}

# runs the matcher over one file and names what it found
check_file()
{
    found=0
    reading="$1"
    if head -1 "$1" | grep -q "^diff --git"; then
        reading=`mktemp`
        grep "^+" "$1" | grep -v "^+++" > "$reading"
    fi
    if grep -n -i -E "\\b($WORDS)\\b" "$reading"; then
        echo "check: the words above are asked never to appear"
        found=1
    fi
    if grep -n -i -E "\\b($SHAPES)\\b" "$reading"; then
        echo "check: the clauses above say how you are speaking, not"
        echo "what you found; cut the clause and let the fact carry"
        echo "itself"
        found=1
    fi
    if [ "$reading" != "$1" ]; then
        rm -f "$reading"
    fi
    return $found
}

# Walks every banned word through every capitalization and every
# surrounding mark, and reports any the matcher misses. The space is
# finite and every point in it is tried, so a miss cannot hide in a
# corner of it. A few ordinary sentences are tried as well, since a
# matcher that flagged everything would pass the first half by itself.
run_self_test()
{
    scratch=`mktemp`
    missed=0
    tried=0
    for word in `echo "$WORDS" | tr '|' ' '`; do
        for shape in "%s" "%s." "(%s)" " %s," "the %s thing"; do
            upper=`echo $word | tr 'a-z' 'A-Z'`
            for form in "$word" "$upper"; do
                printf "a sentence with $shape in it\n" "$form" \
                    > "$scratch"
                tried=`expr $tried + 1`
                if ! grep -q -i -E "\\b($WORDS)\\b" "$scratch"; then
                    echo "self-test: missed [$form] in [$shape]"
                    missed=`expr $missed + 1`
                fi
            done
        done
    done
    cat > "$scratch" <<'CLEANEOF'
The decoder read every packet of the file to its end.
A tone of four hundred and forty cycles a second came back.
Ninety-two frames of ninety-five decode, and three are refused.
The check reports the line and the word it found.
Createdb succeeded and the users table has fourteen columns.
CLEANEOF
    for line in 1 2 3 4 5; do
        sed -n "${line}p" "$scratch" > "$scratch.one"
        tried=`expr $tried + 1`
        if grep -q -i -E "\\b($WORDS)\\b" "$scratch.one" ||
            grep -q -i -E "($SHAPES)" "$scratch.one"; then
            echo "self-test: flagged an ordinary sentence, line $line"
            missed=`expr $missed + 1`
        fi
    done
    rm -f "$scratch" "$scratch.one"
    if [ "$missed" -eq 0 ]; then
        echo "self-test: $tried cases tried, none missed"
        return 0
    fi
    echo "self-test: $missed of $tried cases went wrong"
    return 1
}

if [ "$1" = "--test" ]; then
    run_self_test
    exit $?
fi
draft=${1:-}
if [ -z "$draft" ] || [ ! -f "$draft" ]; then
    echo "check: name a file holding the draft"
    exit 2
fi
if check_file "$draft"; then
# A review owed to Chris has to appear in the response. Recording it
# cleared the count once, which let a review reach a file and never
# reach him. The count clears here, where the response is read.
owed=${REVIEW_OWED:-/home/claude/review_owed.txt}
rounds=${ROUND_COUNT:-/home/claude/round_count.txt}
if [ -s "$owed" ]; then
    carried=0
    while read -r line
    do
        piece=`echo "$line" | cut -c1-40`
        if [ ${#piece} -gt 20 ] && grep -qF "$piece" "$1"; then
            carried=`expr $carried + 1`
        fi
    done < "$owed"
    if [ "$carried" -lt 3 ]; then
        echo "check: a review is owed to Chris and this response does"
        echo "  not carry it. Put the review in the response."
        exit 1
    fi
    printf "0 shown\n" > "$rounds"
    rm -f "$owed"
    echo "check: the review is in the response; the count is 0"
fi
    echo "check: nothing found"
    # The stamp names the round, so a check run for an earlier round
    # cannot stand in for this one.
    if [ -f "$SAID" ]; then
        cksum < "$SAID" | awk '{print $1"-"$2}' > "$STAMP"
    else
        echo "none" > "$STAMP"
    fi
    exit 0
fi
exit 1
X