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

@@ -11,19 +11,27 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
IDENTITY_FILE="$REPO_ROOT/.claude/identity.json"
# Functional-error logger. MUST stay silent on stdout (this script's stdout is
# eval'd by the caller) — log-skill-error.sh prints only to stderr, and we
# redirect everything to /dev/null to be safe.
_logerr() { bash "$REPO_ROOT/.claude/scripts/log-skill-error.sh" "rmm-auth" "$@" >/dev/null 2>&1 || true; }
if [ ! -f "$IDENTITY_FILE" ]; then
_logerr "identity.json not found; RMM auth cannot resolve vault" --context "path=$IDENTITY_FILE"
echo "export TOKEN=''; export RMM=''; export REPO_ROOT=''; echo '[ERROR] identity.json not found' >&2"
exit 1
fi
VAULT_PATH=$(jq -r '.vault_path // empty' "$IDENTITY_FILE")
if [ -z "$VAULT_PATH" ]; then
_logerr "vault_path not in identity.json; RMM auth failed" --context "path=$IDENTITY_FILE"
echo "export TOKEN=''; export RMM=''; export REPO_ROOT=''; echo '[ERROR] vault_path not in identity.json' >&2"
exit 1
fi
VAULT_SH="$VAULT_PATH/scripts/vault.sh"
if [ ! -f "$VAULT_SH" ]; then
_logerr "vault.sh not found at resolved vault_path; RMM auth failed" --context "path=$VAULT_SH"
echo "export TOKEN=''; export RMM=''; export REPO_ROOT=''; echo '[ERROR] vault.sh not found at $VAULT_SH' >&2"
exit 1
fi
@@ -35,6 +43,7 @@ RMM_EMAIL=$(bash "$VAULT_SH" get-field infrastructure/gururmm-server.sops.yaml c
RMM_PASS=$(bash "$VAULT_SH" get-field infrastructure/gururmm-server.sops.yaml credentials.gururmm-api.admin-password 2>/dev/null)
if [ -z "$RMM_EMAIL" ] || [ -z "$RMM_PASS" ]; then
_logerr "vault read of GuruRMM API credentials failed (empty email/password)" --context "entry=infrastructure/gururmm-server.sops.yaml"
echo "export TOKEN=''; export RMM=''; export REPO_ROOT=''; echo '[ERROR] Failed to get RMM credentials from vault' >&2"
exit 1
fi
@@ -45,6 +54,7 @@ JWT=$(curl -s -X POST "$RMM_URL/api/auth/login" -H "Content-Type: application/js
TOKEN=$(echo "$JWT" | jq -r '.token // empty')
if [ -z "$TOKEN" ]; then
_logerr "RMM login failed (no token returned from /api/auth/login)" --context "url=$RMM_URL resp=${JWT:0:80}"
echo "export TOKEN=''; export RMM=''; export REPO_ROOT=''; echo '[ERROR] RMM login failed: $JWT' >&2"
exit 1
fi