PR #5224 (clay/research-2026-04-29) has dual-approve verdicts but stalled in status='conflict' on a stale-ref ff-push failure. conflict_rebase_attempts hit the 3-attempt cap. The failure is mechanical (main moved during merge), not content — eval gate already passed, cherry-pick onto fresh main HEAD should resolve. 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' Two safety guards: - Branch must match clay/research-2026-04-29 (refuses to rewrite an unrelated PR if #5224 was renumbered) - Both verdicts must be 'approve' (refuses to set status='approved' on a PR the eval gate didn't pass) Idempotent. Mirrors reset-m3taversal-sourcer.py shape per the reviewable-mutations standing rule (Ganymede, Apr 27). Per Ganymede observation #3 in the May 10 reaper-allowlist review. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
129 lines
4.7 KiB
Python
Executable file
129 lines
4.7 KiB
Python
Executable file
#!/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()
|