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 <noreply@anthropic.com>
This commit is contained in:
openhands 2026-03-15 17:02:19 +00:00
parent 9ee1429604
commit 4d0390c4fa
2 changed files with 20 additions and 4 deletions

View file

@ -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

View file

@ -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")