Compare commits
1 commit
main
...
epimetheus
| Author | SHA1 | Date | |
|---|---|---|---|
| 545232f61e |
1 changed files with 129 additions and 0 deletions
129
scripts/clear-stale-ref-pr-5224.py
Executable file
129
scripts/clear-stale-ref-pr-5224.py
Executable file
|
|
@ -0,0 +1,129 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Reset PR #5224 (clay/research-2026-04-29) for merge retry after stale-ref conflict.
|
||||
|
||||
Background: PR #5224 has dual-approve verdicts (leo:approve, domain:approve) but
|
||||
sat in status='conflict' with last_error showing a stale-ref ff-push failure:
|
||||
"local ff-push failed: cannot lock ref 'refs/heads/main': is at 28e6fa93 but
|
||||
expected efd613a6". Main moved during the merge attempt, conflict_rebase_attempts
|
||||
hit the 3-attempt cap, and the PR stalled.
|
||||
|
||||
The dual-approve verdicts mean the eval gate already passed — the failure was
|
||||
purely mechanical (ff-push race, not content-related). Cherry-pick onto a fresh
|
||||
main HEAD should resolve cleanly.
|
||||
|
||||
This script resets the conflict state so the merge cycle picks the PR back up:
|
||||
- status: 'conflict' → 'approved'
|
||||
- conflict_rebase_attempts: N → 0
|
||||
- last_error: cleared
|
||||
- audit_log entry: stage='ops', event='pr_5224_stale_ref_reset'
|
||||
|
||||
If cherry-pick fails again on retry, merge cycle will set status='conflict' with
|
||||
a fresh last_error, and we close the PR as stale (downstream clay sessions on
|
||||
2026-04-30, 2026-05-09 etc. have already merged similar content).
|
||||
|
||||
Per Ganymede observation #3 (May 10): DB mutations go through reviewable code
|
||||
paths. Audit trail benefits from one-shape-per-commit.
|
||||
|
||||
Idempotent — safe to re-run. If status is already 'approved' (or merged/closed),
|
||||
prints current state and exits without writing.
|
||||
|
||||
Usage:
|
||||
python3 scripts/clear-stale-ref-pr-5224.py --dry-run
|
||||
python3 scripts/clear-stale-ref-pr-5224.py
|
||||
"""
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import sqlite3
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
DB_PATH = os.environ.get("PIPELINE_DB", "/opt/teleo-eval/pipeline/pipeline.db")
|
||||
PR_NUMBER = 5224
|
||||
EXPECTED_BRANCH = "clay/research-2026-04-29"
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--dry-run", action="store_true")
|
||||
args = parser.parse_args()
|
||||
|
||||
if not Path(DB_PATH).exists():
|
||||
print(f"ERROR: DB not found at {DB_PATH}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
conn = sqlite3.connect(DB_PATH, timeout=30)
|
||||
conn.row_factory = sqlite3.Row
|
||||
|
||||
row = conn.execute(
|
||||
"""SELECT number, branch, status, leo_verdict, domain_verdict,
|
||||
conflict_rebase_attempts, last_error
|
||||
FROM prs WHERE number = ?""",
|
||||
(PR_NUMBER,),
|
||||
).fetchone()
|
||||
if not row:
|
||||
print(f" No PR row for #{PR_NUMBER} — nothing to reset.")
|
||||
return
|
||||
|
||||
print(f" PR #{row['number']} ({row['branch']})")
|
||||
print(f" status={row['status']} leo={row['leo_verdict']} domain={row['domain_verdict']}")
|
||||
print(f" conflict_rebase_attempts={row['conflict_rebase_attempts']}")
|
||||
if row["last_error"]:
|
||||
print(f" last_error={row['last_error'][:140]}")
|
||||
|
||||
# Sanity: branch must match. If someone reused PR #5224 number we don't want
|
||||
# to rewrite an unrelated PR's state.
|
||||
if row["branch"] != EXPECTED_BRANCH:
|
||||
print(
|
||||
f" ABORT: branch mismatch — expected {EXPECTED_BRANCH}, "
|
||||
f"got {row['branch']}. Refusing to write."
|
||||
)
|
||||
sys.exit(2)
|
||||
|
||||
# Sanity: dual-approve must hold. Reset is only safe when eval gate already passed.
|
||||
if not (row["leo_verdict"] == "approve" and row["domain_verdict"] == "approve"):
|
||||
print(
|
||||
f" ABORT: PR is not dual-approve "
|
||||
f"(leo={row['leo_verdict']}, domain={row['domain_verdict']}). "
|
||||
"Refusing to reset to 'approved' status."
|
||||
)
|
||||
sys.exit(2)
|
||||
|
||||
if row["status"] in ("approved", "merging", "merged", "closed"):
|
||||
print(f" Already at status={row['status']} — no-op.")
|
||||
return
|
||||
|
||||
if args.dry_run:
|
||||
print(
|
||||
" (dry-run) UPDATE prs SET status='approved', "
|
||||
"conflict_rebase_attempts=0, last_error=NULL WHERE number=5224"
|
||||
)
|
||||
return
|
||||
|
||||
conn.execute(
|
||||
"""UPDATE prs SET
|
||||
status = 'approved',
|
||||
conflict_rebase_attempts = 0,
|
||||
last_error = NULL,
|
||||
last_attempt = datetime('now')
|
||||
WHERE number = ?""",
|
||||
(PR_NUMBER,),
|
||||
)
|
||||
conn.execute(
|
||||
"""INSERT INTO audit_log (timestamp, stage, event, detail)
|
||||
VALUES (datetime('now'), 'ops', 'pr_5224_stale_ref_reset', ?)""",
|
||||
(json.dumps({
|
||||
"pr": PR_NUMBER,
|
||||
"branch": EXPECTED_BRANCH,
|
||||
"from_status": row["status"],
|
||||
"from_conflict_rebase_attempts": row["conflict_rebase_attempts"],
|
||||
"previous_error": (row["last_error"] or "")[:200],
|
||||
"rationale": "stale-ref ff-push race; dual-approve eval already passed; retry cherry-pick on fresh main",
|
||||
}),),
|
||||
)
|
||||
conn.commit()
|
||||
print(" Reset applied. Merge cycle will pick up on next interval (~30s).")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Reference in a new issue