test(git-tools): classify tool failure at every needle site (#1380-FF)
ci/woodpecker/pr/ci Pipeline was successful

Pipeline 2637 red on a SECOND needle arm (home_case symlink message)
proved the flake shape is not one site: every 'if ! ... | grep -Fq'
assertion in the harness printed the identical FAIL line for a transient
grep failure (rc>=2, fork/alloc under node pressure) and a real mismatch.
The retry/classify helper is hoisted to one shared function and applied
at all three needle sites (fixture loop, home_case, standalone case):
retry tool errors 3x, rc=1 is the real mismatch, persistent tool failure
reports TOOL-ERROR (fail stays 1). 292/292 x2 + busybox.
This commit is contained in:
2026-08-24 16:17:54 -05:00
parent b5792537b0
commit 80752188b1
@@ -515,6 +515,25 @@ FIXTURES="$TMP/fixtures.tsv"
printf '0\t{"tool_input":{"command":"curl -s https://git.example.invalid/api/v1/repos/a/b/issues?q=a%%20b"}}\ta percent-escape in a READ is not this hook'"'"'s business\n'
} > "$FIXTURES"
# Flake containment (#1380-FF, pipelines 2635/2637): a transient failure of the
# ASSERTION TOOLING (grep rc>=2 — fork/alloc error under node pressure) is not a
# contract drift, but the `if ! ... | grep -Fq` shape printed the identical
# FAIL line for both, failing merge verifies on correct guard output. Classify
# instead: retry tool errors 3x; rc=1 is the real mismatch; persistent tool
# failure reports TOOL-ERROR (fail stays 1 — never green on infra noise — but
# the line names the class so a re-run can be judged, not debugged).
assert_out_contains() { # needle why
local attempt rc1
for attempt in 1 2 3; do
printf '%s' "$out" | grep -Fq -- "$1" && return 0
rc1=$?
[ "$rc1" -eq 1 ] && return 1 # grep answered NO — real mismatch
sleep 0.2 # rc>=2: grep itself errored — retry
done
printf 'TOOL-ERROR %s (assertion grep failed 3x — infra/tooling, not guard drift)\n' "$2" >&2
return 2
}
fail=0 n=0
while IFS=$'\t' read -r want payload why remedy; do
[ -n "${want:-}" ] || continue
@@ -530,17 +549,6 @@ while IFS=$'\t' read -r want payload why remedy; do
# if it never succeeds the case reports a TOOL-ERROR line (fail=1 stays, so
# the run is never green-on-infra-noise, but the line names the real class
# and a re-run can be judged instead of debugged as a guard defect).
assert_out_names() {
local attempt rc1
for attempt in 1 2 3; do
printf '%s' "$out" | grep -Fq -- "$1" && return 0
rc1=$?
[ "$rc1" -eq 1 ] && return 1 # grep answered NO — real mismatch
sleep 0.2 # rc>=2: grep itself errored — retry
done
printf 'TOOL-ERROR %s (assertion grep failed 3x; guard output was correct class — infra/tooling, not guard drift)\n' "$why" >&2
return 2
}
if [ "$got" != "$want" ]; then
printf 'FAIL %s (want exit %s, got %s)\n' "$why" "$want" "$got"
fail=1
@@ -551,7 +559,7 @@ while IFS=$'\t' read -r want payload why remedy; do
# so /issues/1/labels blocking with "use issue-create.sh" passed every run for
# six rounds. Where a fixture states the remediation it expects, assert it.
if [ -n "${remedy:-}" ]; then
assert_out_names "$remedy"
assert_out_contains "$remedy" "$why"
arcret=$?
if [ "$arcret" -eq 1 ]; then
printf 'FAIL %s (blocked, but the advice does not name %s)\n' "$why" "$remedy"
@@ -597,10 +605,15 @@ home_case() {
fail=1
return
fi
if [ -n "$needle" ] && ! printf '%s' "$out" | grep -Fq -- "$needle"; then
printf 'FAIL %s (exit %s, but the message does not say %s)\n' "$why" "$got" "$needle"
fail=1
return
if [ -n "$needle" ]; then
assert_out_contains "$needle" "$why"
arcret=$?
if [ "$arcret" -eq 1 ]; then
printf 'FAIL %s (exit %s, but the message does not say %s)\n' "$why" "$got" "$needle"
fail=1
return
fi
[ "$arcret" -eq 0 ] || { fail=1; return; }
fi
printf 'ok %s\n' "$why"
}
@@ -690,10 +703,15 @@ lone_case() {
fail=1
return
fi
if [ -n "$needle" ] && ! printf '%s' "$out" | grep -Fq -- "$needle"; then
printf 'FAIL %s [standalone] (exit %s, but the message does not say %s)\n' "$why" "$got" "$needle"
fail=1
return
if [ -n "$needle" ]; then
assert_out_contains "$needle" "$why"
arcret=$?
if [ "$arcret" -eq 1 ]; then
printf 'FAIL %s [standalone] (exit %s, but the message does not say %s)\n' "$why" "$got" "$needle"
fail=1
return
fi
[ "$arcret" -eq 0 ] || { fail=1; return; }
fi
printf 'ok %s [standalone]\n' "$why"
}