diff --git a/adam/tests/run-tests.sh b/adam/tests/run-tests.sh index a9061c6..023a4b2 100755 --- a/adam/tests/run-tests.sh +++ b/adam/tests/run-tests.sh @@ -631,6 +631,27 @@ else echo " FAIL: hex/epoch fingerprint mismatch (a=$fp_a b=$fp_b)"; FAIL=$((FAIL+1)) fi +# --- Test 36b: fingerprint — file-tool success objects are not errors --- +echo "Test 36b: Read/Edit/Write success objects with error words return null" +fp_r=$(node -e "import('$HOOK').then(m => console.log(m.errorFingerprint({type:'text',file:{filePath:'/x/cache.go',content:'return fmt.Errorf(\"failed: not found\")'}})))") +fp_e=$(node -e "import('$HOOK').then(m => console.log(m.errorFingerprint({filePath:'/x/a.ts',oldString:'throw new Error(1)',newString:'x',originalFile:'undefined error'})))") +fp_w=$(node -e "import('$HOOK').then(m => console.log(m.errorFingerprint({type:'create',filePath:'/x/b.ts',content:'const errorRateTier = \"failed\"'})))") +if [ "$fp_r" = "null" ] && [ "$fp_e" = "null" ] && [ "$fp_w" = "null" ]; then + echo " PASS: file-tool success shapes not fingerprinted"; PASS=$((PASS+1)) +else + echo " FAIL: success shape fingerprinted (r=$fp_r e=$fp_e w=$fp_w)"; FAIL=$((FAIL+1)) +fi + +# --- Test 36c: fingerprint — real error shapes still fingerprinted --- +echo "Test 36c: is_error:true with filePath and plain error strings still fingerprint" +fp_x=$(node -e "import('$HOOK').then(m => console.log(m.errorFingerprint({is_error:true,filePath:'/x/a.ts',content:'String to replace not found in file.'})))") +fp_s=$(node -e "import('$HOOK').then(m => console.log(m.errorFingerprint('Error: result (70,310 characters) exceeds maximum allowed tokens')))") +if [ "$fp_x" != "null" ] && [ "$fp_s" != "null" ]; then + echo " PASS: error shapes keep fingerprints ($fp_x, $fp_s)"; PASS=$((PASS+1)) +else + echo " FAIL: error shape lost fingerprint (x=$fp_x s=$fp_s)"; FAIL=$((FAIL+1)) +fi + # --- Test 37: correction corpus — strong tokens fire --- echo "Test 37: strong-correction tokens each emit correction signal" strong_ok=1 diff --git a/hooks/adam-observe.mjs b/hooks/adam-observe.mjs index 386a65b..b99bb47 100755 --- a/hooks/adam-observe.mjs +++ b/hooks/adam-observe.mjs @@ -249,6 +249,12 @@ function snapshotContext(state) { function errorFingerprint(toolResponse) { if (!toolResponse) return null; + // Read/Edit/Write success objects carry `file`/`filePath` and no `is_error`. + // PostToolUse never receives failed calls (CC routes them to + // PostToolUseFailure), so ERROR_RE here would only match source-file text. + if (typeof toolResponse === "object" && !Array.isArray(toolResponse) && + toolResponse.is_error !== true && + ("file" in toolResponse || "filePath" in toolResponse)) return null; let text = ""; if (typeof toolResponse === "string") text = toolResponse; else if (toolResponse.content !== undefined) {