From 2d409a4e7a2f7bd812fdc55fe12d93dffc1a1487 Mon Sep 17 00:00:00 2001 From: Mike Swanson Date: Fri, 5 Jun 2026 08:32:28 -0700 Subject: [PATCH] fix(grok): self-healing embed fallback for review modes If a grok read_file-based review (review/review-files/review-diff) returns empty (the 0.2.20-style headless tool-gating regression), retry once with the file(s)/diff embedded inline via the no-tools text path, when content is under 256KB; otherwise emit a clear skip note. Keeps grok-reads-files as the default happy path (works on 0.2.22) and degrades gracefully instead of returning silence. text/verify/raw unchanged; Windows path handling intact. Co-Authored-By: Claude Opus 4.8 (1M context) --- .claude/skills/grok/scripts/ask-grok.sh | 78 +++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/.claude/skills/grok/scripts/ask-grok.sh b/.claude/skills/grok/scripts/ask-grok.sh index 3143f86..239caf5 100644 --- a/.claude/skills/grok/scripts/ask-grok.sh +++ b/.claude/skills/grok/scripts/ask-grok.sh @@ -114,6 +114,40 @@ find_artifact() { ls -t "$HOME/.grok/sessions/"*"/$1/$2/"* 2>/dev/null | head -1 } +# --- self-healing embed fallback for review modes ----------------------------- +# The review/review-files/review-diff modes default to letting grok read the +# target files/diff ITSELF (read_file tool) — this works on grok >=0.2.22 and +# avoids stuffing large files into the prompt. But on grok 0.2.20 headless +# read_file wasn't wired, so those runs came back EMPTY (silent failure). The +# text/verify modes never had this problem because they EMBED all content inline +# (no tools). To survive a future regression of that kind, each review mode below +# retries ONCE with the file/diff contents embedded inline (the no-tools text +# path) when the grok-reads-files run returns empty — but only when the payload +# is small enough to safely inline (EMBED_FALLBACK_MAX_BYTES). Over that size we +# keep the existing behavior (report "no result") rather than blow up the prompt. +EMBED_FALLBACK_MAX_BYTES=262144 # ~256KB ceiling for inlining content into the prompt + +# byte size of one or more files, summed; prints an integer (0 if none readable). +bytes_of_files() { + local total=0 n + for f in "$@"; do + n="$(wc -c < "$f" 2>/dev/null || echo 0)" + n="${n//[^0-9]/}"; [ -z "$n" ] && n=0 + total=$(( total + n )) + done + printf '%s' "$total" +} + +# Run grok in the no-tools text path against the already-built $PF, capturing the +# result into the caller's variable. Mirrors the text-mode invocation (web search +# off, short turn budget) since everything it needs is already in the prompt. +# Resets RUN_CWD to a neutral working dir so no tool-reachable cwd is implied. +embed_fallback_run() { + RUN_CWD="$WORK" + run_grok 240 --disable-web-search --max-turns 3 + jfield text +} + case "$MODE" in text|verify) # content from --prompt-file (good for long docs) or the positional arg @@ -183,6 +217,18 @@ case "$MODE" in printf 'Use your read_file tool to read the file at this absolute path, then do the task and stop. You may also read closely-related files it references if that helps. Do not modify anything.\nPath: %s\n\nTask: %s' "$tgt_win" "$instr" > "$PF" run_grok 240 --max-turns 12 txt="$(jfield text)" + if [ -z "$txt" ]; then + # grok-reads-files came back empty (possible read_file regression) -> retry + # ONCE with the file contents embedded inline, if small enough to inline. + sz="$(bytes_of_files "$resolved")" + if [ "$sz" -le "$EMBED_FALLBACK_MAX_BYTES" ]; then + echo "[$SELF] empty result; retrying with file embedded inline (${sz}B)" >&2 + { printf 'Review the following file. Answer in text only; do not use tools. Do not modify anything.\nPath: %s\n\nTask: %s\n\n=== BEGIN FILE ===\n' "$resolved" "$instr"; cat "$resolved"; printf '\n=== END FILE ===\n'; } > "$PF" + txt="$(embed_fallback_run)" + else + echo "[$SELF] embed-fallback skipped: file is ${sz}B (> ${EMBED_FALLBACK_MAX_BYTES}B threshold)" >&2 + 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 ;; @@ -200,10 +246,12 @@ case "$MODE" in done [ ${#files[@]} -eq 0 ] && { echo "usage: $SELF review-files [-i \"instructions\"] [file ...]" >&2; exit 2; } list="" + resolved_files=() # POSIX paths, kept for the embed fallback (sizing + cat) for f in "${files[@]}"; do if [ -f "$f" ]; then r="$f" elif [ -f "$REPO_ROOT/$f" ]; then r="$REPO_ROOT/$f" else echo "[$SELF] file not found: $f" >&2; exit 2; fi + resolved_files+=("$r") list+="- $(winpath "$r") " done @@ -211,6 +259,23 @@ case "$MODE" in printf 'Use your read_file tool to read EACH of these files (absolute paths), then perform the task across ALL of them and stop. Do not modify anything.\n\nFiles:\n%s\nTask: %s' "$list" "$instr" > "$PF" run_grok 300 --max-turns 24 txt="$(jfield text)" + if [ -z "$txt" ]; then + # read_file path empty -> retry ONCE with all file contents embedded inline, + # if the combined size is under the inline threshold. + sz="$(bytes_of_files "${resolved_files[@]}")" + if [ "$sz" -le "$EMBED_FALLBACK_MAX_BYTES" ]; then + echo "[$SELF] empty result; retrying with ${#resolved_files[@]} file(s) embedded inline (${sz}B)" >&2 + { + printf 'Review the following files together as a unit. Answer in text only; do not use tools. Do not modify anything.\n\nTask: %s\n' "$instr" + for r in "${resolved_files[@]}"; do + printf '\n=== BEGIN FILE: %s ===\n' "$r"; cat "$r"; printf '\n=== END FILE: %s ===\n' "$r" + done + } > "$PF" + txt="$(embed_fallback_run)" + else + echo "[$SELF] embed-fallback skipped: combined files are ${sz}B (> ${EMBED_FALLBACK_MAX_BYTES}B threshold)" >&2 + 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 ;; @@ -243,6 +308,19 @@ case "$MODE" in { printf 'Review the following unified git diff. %s\nYou may use read_file on any changed file (paths in the diff are relative to your current directory; strip the a/ b/ prefixes) for full context. Do not modify anything.\n\n=== BEGIN DIFF ===\n' "$instr"; cat "$TMP/diff.txt"; printf '\n=== END DIFF ===\n'; } > "$PF" run_grok 300 --max-turns 20 txt="$(jfield text)" + if [ -z "$txt" ]; then + # If even the diff review (which already embeds the diff but invites read_file + # for context) came back empty, retry ONCE in the strict no-tools text path + # with just the diff inline, provided the diff is under the inline threshold. + sz="$(bytes_of_files "$TMP/diff.txt")" + if [ "$sz" -le "$EMBED_FALLBACK_MAX_BYTES" ]; then + echo "[$SELF] empty result; retrying with diff embedded inline, no tools (${sz}B)" >&2 + { printf 'Review the following unified git diff. %s\nAnswer in text only; do not use tools. Do not modify anything.\n\n=== BEGIN DIFF ===\n' "$instr"; cat "$TMP/diff.txt"; printf '\n=== END DIFF ===\n'; } > "$PF" + txt="$(embed_fallback_run)" + else + echo "[$SELF] embed-fallback skipped: diff is ${sz}B (> ${EMBED_FALLBACK_MAX_BYTES}B threshold)" >&2 + 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 ;;