harness: fleet-wide functional-error + correction + friction logging

Add .claude/scripts/log-skill-error.sh — the canonical agent error log helper
(writes errorlog.md in DATE | MACHINE | skill | [type] error format, soft-fails).
Three categories: execution failures (default), user corrections (--correction),
and preventable self-inflicted friction (--friction; cite ref= when it repeats a
documented gotcha). Goal: stop paying tokens twice for the same avoidable mistake.

- CLAUDE.md: make logging mandatory for all skills + corrections + friction.
- skill-creator: new skills must wire in the helper (guidance + checklist).
- Retrofit every skill script's genuine failure branches to call the helper
  (b2/bitdefender/mailprotector/packetdial/coord python CLIs; remediation-tool
  + onboard365 bash; vault, rmm-auth, post-bot-alert, agy, grok, 1password,
  run-onboarding-diagnostic). Handled conditions + self-tests left alone.
- errorlog.md: broaden header to cover skills + harness + corrections; seed this
  session's corrections (INKY, Mail.Send token-audience, omnibox-strictness) and
  friction (git-bash /tmp, env-persistence, argv-limit, PowerShell var-case).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-15 11:39:43 -07:00
parent 927a06a0cf
commit 9960da5f9a
29 changed files with 388 additions and 36 deletions

View File

@@ -83,6 +83,8 @@ WORK="$TMP/work"; mkdir -p "$WORK"
PF="$TMP/prompt.txt"; OUT="$TMP/out.json"
RUN_CWD="$WORK" # grok's working dir; the 'review' mode overrides to the repo so read_file can reach repo files
REPO_ROOT="${CLAUDETOOLS_ROOT:-$(cd "$SCRIPT_DIR/../../../.." 2>/dev/null && pwd)}"
# Functional-error logger (skill name "grok"); soft-fails, never breaks the caller.
_logerr() { bash "$REPO_ROOT/.claude/scripts/log-skill-error.sh" "grok" "$@" >/dev/null 2>&1 || true; }
# run grok headless. $1=timeout secs; rest=extra flags. Reads $PF -> $OUT.
# Never fails the script on grok's exit code (Cancelled is expected; we read artifacts).
@@ -170,7 +172,7 @@ case "$MODE" in
run_grok 180 --disable-web-search --max-turns 3
txt="$(jfield text)"
if [ -n "$txt" ]; then printf '%s\n' "$txt"; else
echo "[$SELF] no text (stopReason=$(jfield stopReason)); raw: $OUT" >&2; exit 1; fi
echo "[$SELF] no text (stopReason=$(jfield stopReason)); raw: $OUT" >&2; _logerr "grok returned no text" --context "mode=$MODE stopReason=$(jfield stopReason)"; exit 1; fi
;;
image)
[ -z "${1:-}" ] && { echo "usage: $SELF image \"<prompt>\" [out.png]" >&2; exit 2; }
@@ -180,7 +182,7 @@ case "$MODE" in
sid="$(jfield sessionId)"; art="$(find_artifact "$sid" images)"
if [ -n "$art" ] && [ -f "$art" ]; then cp -f "$art" "$out"
echo "[$SELF] image OK -> $out (session $sid)"
else echo "[$SELF] no image artifact (session=$sid, stopReason=$(jfield stopReason))" >&2; exit 1; fi
else echo "[$SELF] no image artifact (session=$sid, stopReason=$(jfield stopReason))" >&2; _logerr "grok produced no image artifact" --context "session=$sid stopReason=$(jfield stopReason)"; exit 1; fi
;;
video)
[ -z "${1:-}" ] || [ -z "${2:-}" ] && { echo "usage: $SELF video \"<prompt>\" <input-image> [out.mp4]" >&2; exit 2; }
@@ -192,7 +194,7 @@ case "$MODE" in
sid="$(jfield sessionId)"; art="$(find_artifact "$sid" videos)"
if [ -n "$art" ] && [ -f "$art" ]; then cp -f "$art" "$out"
echo "[$SELF] video OK -> $out (session $sid)"
else echo "[$SELF] no video artifact (session=$sid, stopReason=$(jfield stopReason))" >&2; exit 1; fi
else echo "[$SELF] no video artifact (session=$sid, stopReason=$(jfield stopReason))" >&2; _logerr "grok produced no video artifact" --context "session=$sid stopReason=$(jfield stopReason)"; exit 1; fi
;;
xsearch)
[ -z "${1:-}" ] && { echo "usage: $SELF xsearch \"<query>\"" >&2; exit 2; }
@@ -200,7 +202,7 @@ case "$MODE" in
run_grok 150 --max-turns 6
txt="$(jfield text)"
if [ -n "$txt" ]; then printf '%s\n' "$txt"; else
echo "[$SELF] no result (stopReason=$(jfield stopReason))" >&2; exit 1; fi
echo "[$SELF] no result (stopReason=$(jfield stopReason))" >&2; _logerr "grok xsearch returned no result" --context "mode=xsearch stopReason=$(jfield stopReason)"; exit 1; fi
;;
review|file)
[ -z "${1:-}" ] && { echo "usage: $SELF review <file-path> [instructions]" >&2; exit 2; }
@@ -233,7 +235,7 @@ case "$MODE" in
fi
fi
if [ -n "$txt" ]; then printf '%s\n' "$txt"; else
echo "[$SELF] no result (session=$(jfield sessionId), stopReason=$(jfield stopReason))" >&2; exit 1; fi
echo "[$SELF] no result (session=$(jfield sessionId), stopReason=$(jfield stopReason))" >&2; _logerr "grok review returned no result" --context "mode=$MODE session=$(jfield sessionId) stopReason=$(jfield stopReason)"; exit 1; fi
;;
review-files)
# review-files [-i "instructions"] <file> [file ...]
@@ -283,7 +285,7 @@ case "$MODE" in
fi
fi
if [ -n "$txt" ]; then printf '%s\n' "$txt"; else
echo "[$SELF] no result (session=$(jfield sessionId), stopReason=$(jfield stopReason))" >&2; exit 1; fi
echo "[$SELF] no result (session=$(jfield sessionId), stopReason=$(jfield stopReason))" >&2; _logerr "grok review returned no result" --context "mode=$MODE session=$(jfield sessionId) stopReason=$(jfield stopReason)"; exit 1; fi
;;
review-diff)
# review-diff [-C <repo-dir>] [-i "instructions"] <gitref> [-- <pathspec...>]
@@ -328,7 +330,7 @@ case "$MODE" in
fi
fi
if [ -n "$txt" ]; then printf '%s\n' "$txt"; else
echo "[$SELF] no result (session=$(jfield sessionId), stopReason=$(jfield stopReason))" >&2; exit 1; fi
echo "[$SELF] no result (session=$(jfield sessionId), stopReason=$(jfield stopReason))" >&2; _logerr "grok review returned no result" --context "mode=$MODE session=$(jfield sessionId) stopReason=$(jfield stopReason)"; exit 1; fi
;;
raw)
"$GROK" "$@"