rhodawk-ai-devops-engine / conviction_engine.py
Rhodawk AI
feat: v4.0 Peak enhancements + Antagonist mode
8a23fcf
Raw
History Blame Contribute Delete
5.24 kB
"""
Rhodawk AI — Conviction Engine (Auto-Merge Gate)
================================================
Evaluates whether a successfully verified fix meets the conviction threshold
for autonomous merge without human review.
Conviction criteria (all must be met):
1. adversarial_confidence >= CONVICTION_CONFIDENCE_MIN (default 0.92)
2. adversarial_verdict == "APPROVE" (no conditional)
3. consensus_fraction >= CONVICTION_CONSENSUS_MIN (default 0.85 — 3/3 models agree)
4. Memory engine found a semantically identical past fix that was human-merged
(similarity >= CONVICTION_MEMORY_MIN, default 0.85)
5. test_attempts == 1 (fixed on first try — indicates clean, well-understood fix)
6. SAST findings == 0 (zero informational findings on the diff)
7. No new packages introduced in the diff
When all criteria pass, auto_merge() is called which uses the GitHub API to
merge the PR directly (no human required).
Enable with: RHODAWK_AUTO_MERGE=true
"""
import os
import time
import requests
CONVICTION_CONFIDENCE_MIN = float(os.getenv("RHODAWK_CONVICTION_CONFIDENCE", "0.92"))
CONVICTION_CONSENSUS_MIN = float(os.getenv("RHODAWK_CONVICTION_CONSENSUS", "0.85"))
CONVICTION_MEMORY_MIN = float(os.getenv("RHODAWK_CONVICTION_MEMORY_SIM", "0.85"))
AUTO_MERGE_ENABLED = os.getenv("RHODAWK_AUTO_MERGE", "false").lower() == "true"
def evaluate_conviction(
adversarial_review: dict,
similar_fixes: list[dict],
test_attempts: int,
sast_findings_count: int,
new_packages: list[str],
) -> tuple[bool, str]:
"""
Returns (should_auto_merge, reason_string).
All criteria must pass for auto-merge to be approved.
"""
if not AUTO_MERGE_ENABLED:
return False, "auto-merge disabled (RHODAWK_AUTO_MERGE != true)"
verdict = adversarial_review.get("verdict", "CONDITIONAL")
confidence = float(adversarial_review.get("confidence", 0.0))
consensus_fraction = float(adversarial_review.get("consensus_fraction", 0.0))
checks: list[tuple[bool, str]] = [
(verdict == "APPROVE",
f"adversarial_verdict must be APPROVE, got {verdict}"),
(confidence >= CONVICTION_CONFIDENCE_MIN,
f"adversarial_confidence {confidence:.3f} < {CONVICTION_CONFIDENCE_MIN}"),
(consensus_fraction >= CONVICTION_CONSENSUS_MIN,
f"consensus_fraction {consensus_fraction:.3f} < {CONVICTION_CONSENSUS_MIN}"),
(test_attempts == 1,
f"fixed in {test_attempts} attempt(s), require 1"),
(sast_findings_count == 0,
f"SAST has {sast_findings_count} finding(s), require 0"),
(not new_packages,
f"diff introduces new packages: {new_packages}"),
]
memory_check_passed = False
best_memory_sim = 0.0
for fix in similar_fixes:
sim = float(fix.get("similarity", 0.0))
if sim >= CONVICTION_MEMORY_MIN:
memory_check_passed = True
best_memory_sim = sim
break
checks.append((
memory_check_passed,
f"no human-merged memory match with similarity >= {CONVICTION_MEMORY_MIN} "
f"(best found: {best_memory_sim:.3f})"
))
failed = [(passed, reason) for passed, reason in checks if not passed]
if failed:
reasons = "; ".join(r for _, r in failed)
return False, f"conviction not met: {reasons}"
return True, (
f"all {len(checks)} conviction criteria passed "
f"(confidence={confidence:.3f}, consensus={consensus_fraction:.3f}, "
f"memory_sim={best_memory_sim:.3f})"
)
def auto_merge_pr(
repo: str,
pr_url: str,
token: str,
merge_method: str = "squash",
) -> tuple[bool, str]:
"""
Merge a PR via GitHub API.
merge_method: "merge" | "squash" | "rebase"
Returns (success, message).
"""
if not pr_url or "github.com" not in pr_url:
return False, f"invalid PR URL: {pr_url}"
try:
parts = pr_url.rstrip("/").split("/")
pr_number = int(parts[-1])
owner = parts[-4]
repo_name = parts[-3]
except (IndexError, ValueError) as e:
return False, f"could not parse PR URL {pr_url}: {e}"
headers = {
"Authorization": f"Bearer {token}",
"Accept": "application/vnd.github+json",
"X-GitHub-API-Version": "2022-11-28",
}
resp = requests.put(
f"https://api.github.com/repos/{owner}/{repo_name}/pulls/{pr_number}/merge",
headers=headers,
json={
"merge_method": merge_method,
"commit_title": f"[Rhodawk] Autonomous merge — conviction threshold met",
"commit_message": (
"This PR was autonomously merged by Rhodawk AI after meeting all "
"conviction criteria:\n"
"- Adversarial consensus review: APPROVED (3/3 models)\n"
"- Test verification: GREEN (1 attempt)\n"
"- SAST gate: CLEAN\n"
"- Memory match: CONFIRMED (prior human-merged fix)\n"
),
},
timeout=30,
)
if resp.status_code in (200, 201):
return True, f"auto-merged PR #{pr_number} via {merge_method}"
return False, f"GitHub merge API returned {resp.status_code}: {resp.text[:200]}"