perf(audit-b3): parallelize unassignment for timely live run
Some checks failed
Agent PR Gate / gate (pull_request) Failing after 1m3s
Self-Healing Smoke / self-healing-smoke (pull_request) Failing after 21s
Smoke Test / smoke (pull_request) Failing after 25s
Agent PR Gate / report (pull_request) Successful in 20s

Uses ThreadPoolExecutor (12 workers) to complete full cap enforcement
within subprocess timeout. Adds progress logging every 50 tasks.
This commit is contained in:
STEP35 Burn Agent
2026-04-30 03:19:07 -04:00
parent 6af101c953
commit 6b729326ad

View File

@@ -125,9 +125,12 @@ def main():
for iss in issues_sorted[:overflow]:
print(f" - #{iss['number']}: {iss.get('title', '')[:50]}")
# Dry-run: show summary and exit
# Dry-run: just show summary and exit
if args.dry_run:
print("\n=== DRY RUN — no changes made ===")
# For dry-run, after = before (no changes)
for agent in by_agent:
by_agent[agent]["after"] = by_agent[agent]["before"]
summary = build_summary(by_agent, unassignment_map)
print("\n" + summary)
if args.output:
@@ -135,26 +138,44 @@ def main():
print(f"\nSummary written to {args.output}")
return 0
# LIVE: perform unassignments and comments
# LIVE: perform unassignments and comments (concurrent)
print("\n=== LIVE RUN — executing ===")
from concurrent.futures import ThreadPoolExecutor, as_completed
import threading
lock = threading.Lock()
tasks = []
for agent, issues_to_unassign in unassignment_map.items():
for iss in issues_to_unassign:
issue_num = iss["number"]
repo_name = next(
(r for r in REPOS if f"/{r}/issues/" in iss.get("html_url", "")), REPOS[0]
)
# Unassign: PATCH with empty assignees
_, status = api("PATCH", f"/repos/{ORG}/{repo_name}/issues/{issue_num}", token, {"assignees": []})
if status not in (200, 201, 204):
print(f" WARNING: unassign #{issue_num} failed (HTTP {status})")
continue
# Comment
comment_body = COMMENT_TEMPLATE.format(assignee=agent)
_, status = api("POST", f"/repos/{ORG}/{repo_name}/issues/{issue_num}/comments", token, {"body": comment_body})
if status not in (200, 201):
print(f" WARNING: comment on #{issue_num} failed (HTTP {status})")
else:
print(f" ✓ Unassigned & commented #{issue_num} ({repo_name})")
tasks.append((agent, issue_num, repo_name, iss))
print(f"Total unassignment tasks: {len(tasks)}")
def do_task(agent, issue_num, repo_name, iss):
# Unassign
_, status1 = api("PATCH", f"/repos/{ORG}/{repo_name}/issues/{issue_num}", token, {"assignees": []})
if status1 not in (200, 201, 204):
return (agent, issue_num, repo_name, False, f"unassign HTTP {status1}")
# Comment
comment_body = COMMENT_TEMPLATE.format(assignee=agent)
_, status2 = api("POST", f"/repos/{ORG}/{repo_name}/issues/{issue_num}/comments", token, {"body": comment_body})
if status2 not in (200, 201):
return (agent, issue_num, repo_name, True, f"unassigned but comment HTTP {status2}")
return (agent, issue_num, repo_name, True, "OK")
completed = 0
with ThreadPoolExecutor(max_workers=12) as executor:
futures = [executor.submit(do_task, a, n, r, i) for (a, n, r, i) in tasks]
for fut in as_completed(futures):
agent, num, repo, ok, msg = fut.result()
with lock:
completed += 1
if completed % 50 == 0:
print(f" Progress: {completed}/{len(tasks)}")
if ok:
print(f" ✓ #{num} ({repo})")
else:
print(f" ✗ #{num} ({repo}): {msg}")
# Recompute after counts for summary
print("\nRecomputing after counts ...")