From 4d0390c4fa207d28b212ea14a9dbe595426adf73 Mon Sep 17 00:00:00 2001 From: openhands Date: Sun, 15 Mar 2026 17:02:19 +0000 Subject: [PATCH] fix: address review findings for cross-candidate red-team sweep (#822) - red-team-sweep.sh: reset CROSS_PATTERNS_FILE at sweep start to prevent stale patterns from prior invocations contaminating a fresh run - red-team-sweep.sh: wrap pattern-extraction Python in set +e/set -e and capture output so log() prefix is applied; move memory truncation outside the if-block so it runs unconditionally even if Python fails - red-team.sh: filter entries where candidate == current_candidate before grouping, removing self-referential cross-candidate evidence - red-team.sh: skip entries with empty pattern key (both pattern and strategy fields empty) to prevent spurious bucket merging Co-Authored-By: Claude Sonnet 4.6 --- scripts/harb-evaluator/red-team-sweep.sh | 19 +++++++++++++++---- scripts/harb-evaluator/red-team.sh | 5 +++++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/scripts/harb-evaluator/red-team-sweep.sh b/scripts/harb-evaluator/red-team-sweep.sh index a392518..f59023b 100755 --- a/scripts/harb-evaluator/red-team-sweep.sh +++ b/scripts/harb-evaluator/red-team-sweep.sh @@ -20,6 +20,9 @@ die() { log "FATAL: $*" >&2; exit 1; } [[ -f "$INJECT" ]] || die "inject.sh not found at $INJECT" +# Reset cross-patterns file for this sweep invocation (prevents stale data from prior runs) +> "$CROSS_PATTERNS_FILE" + # Load progress completed=() if [[ -f "$PROGRESS_FILE" ]]; then @@ -118,7 +121,8 @@ PYEOF # 4b. Extract abstract patterns into cross-candidate file, then clear raw memory if [[ -f "$MEMORY_FILE" && -s "$MEMORY_FILE" ]]; then - python3 - "$MEMORY_FILE" "$CROSS_PATTERNS_FILE" <<'PYEOF' + set +e + _extract_out=$(python3 - "$MEMORY_FILE" "$CROSS_PATTERNS_FILE" <<'PYEOF' import json, sys mem_file = sys.argv[1] @@ -135,16 +139,23 @@ with open(mem_file) as f: pass if not new_entries: - print(" No memory entries to extract") + print("No memory entries to extract") sys.exit(0) with open(cross_file, 'a') as f: for e in new_entries: f.write(json.dumps(e) + '\n') -print(f" Extracted {len(new_entries)} entr{'y' if len(new_entries)==1 else 'ies'} to cross-patterns file") +print(f"Extracted {len(new_entries)} entr{'y' if len(new_entries)==1 else 'ies'} to cross-patterns file") PYEOF - # Clear raw memory so the next candidate starts with a fresh tactical state + ) + _py_exit=$? + set -e + [[ -n "$_extract_out" ]] && log "$_extract_out" + [[ $_py_exit -ne 0 ]] && log "WARNING: cross-pattern extraction failed (exit $_py_exit) — continuing" + fi + # Always clear raw memory so the next candidate starts with a fresh tactical state + if [[ -f "$MEMORY_FILE" ]]; then > "$MEMORY_FILE" log "Cleared raw memory for next candidate" fi diff --git a/scripts/harb-evaluator/red-team.sh b/scripts/harb-evaluator/red-team.sh index a3a1add..a951bb9 100755 --- a/scripts/harb-evaluator/red-team.sh +++ b/scripts/harb-evaluator/red-team.sh @@ -486,10 +486,15 @@ with open(cross_file) as f: if not entries: sys.exit(0) +# Exclude entries from the current candidate (they are cross-candidate evidence, not self-evidence) +entries = [e for e in entries if e.get("candidate", "unknown") != current_candidate] + # Group by abstract pattern; track worked/failed per candidate by_pattern = defaultdict(lambda: {"worked": {}, "failed": {}, "insight": ""}) for e in entries: pat = e.get("pattern", "") or e.get("strategy", "")[:80] + if not pat: + continue # skip entries with no identifiable pattern cand = e.get("candidate", "unknown") prof = e.get("optimizer_profile", "unknown") result = e.get("result", "HELD")