feat: atomic extract-and-connect + stale PR monitor + response audit #4
1 changed files with 24 additions and 6 deletions
30
lib/fixer.py
30
lib/fixer.py
|
|
@ -225,15 +225,33 @@ async def fix_cycle(conn, max_workers=None) -> tuple[int, int]:
|
|||
# Garbage collection: close PRs with exhausted fix budget that are stuck in open.
|
||||
# These were evaluated, rejected, fixer couldn't help, nobody closes them.
|
||||
# (Epimetheus session 2 — prevents zombie PR accumulation)
|
||||
_gc = conn.execute(
|
||||
"""UPDATE prs SET status = 'closed', last_error = 'fix budget exhausted — auto-closed'
|
||||
# Bug fix: must also close on Forgejo + delete branch, not just DB update.
|
||||
# DB-only close caused Forgejo/DB state divergence — branches stayed alive,
|
||||
# blocking Gate 2 in batch-extract for 5 days. (Epimetheus session 4)
|
||||
gc_rows = conn.execute(
|
||||
"""SELECT number, branch FROM prs
|
||||
WHERE status = 'open'
|
||||
AND fix_attempts >= ?
|
||||
AND (domain_verdict = 'request_changes' OR leo_verdict = 'request_changes')""",
|
||||
(config.MAX_FIX_ATTEMPTS + 2,), # GC threshold = mechanical + substantive budget
|
||||
)
|
||||
if _gc.rowcount > 0:
|
||||
logger.info("GC: closed %d exhausted PRs", _gc.rowcount)
|
||||
(config.MAX_FIX_ATTEMPTS + 2,),
|
||||
).fetchall()
|
||||
if gc_rows:
|
||||
from .forgejo import api as _gc_forgejo, repo_path as _gc_repo_path
|
||||
for row in gc_rows:
|
||||
pr_num, branch = row["number"], row["branch"]
|
||||
try:
|
||||
await _gc_forgejo("POST", _gc_repo_path(f"issues/{pr_num}/comments"),
|
||||
{"body": "Auto-closed: fix budget exhausted. Source will be re-extracted."})
|
||||
await _gc_forgejo("PATCH", _gc_repo_path(f"pulls/{pr_num}"), {"state": "closed"})
|
||||
if branch:
|
||||
await _gc_forgejo("DELETE", _gc_repo_path(f"branches/{branch}"))
|
||||
except Exception as e:
|
||||
logger.warning("GC: failed to close PR #%d on Forgejo: %s", pr_num, e)
|
||||
conn.execute(
|
||||
"UPDATE prs SET status = 'closed', last_error = 'fix budget exhausted — auto-closed' WHERE number = ?",
|
||||
(pr_num,),
|
||||
)
|
||||
logger.info("GC: closed %d exhausted PRs (DB + Forgejo + branch cleanup)", len(gc_rows))
|
||||
|
||||
batch_limit = min(max_workers or config.MAX_FIX_PER_CYCLE, config.MAX_FIX_PER_CYCLE)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue