theseus: add Ganymede pre-merge code review gate to evaluate trigger
- What: PRs touching code files (ops/, diagnostics/, .py, .sh, etc.) now get Ganymede code review in addition to Leo + domain agent - Why: Ganymede was reviewing ~30% of code PRs after deploy, not before. This makes code review 100% pre-merge, matching how Leo already gates claims. - How: detect_code_pr() checks file patterns, runs Ganymede with code-focused prompt, adds VERDICT:GANYMEDE gate to merge eligibility check Pentagon-Agent: Theseus <24DE7DA0-E4D5-4023-B1A2-3F736AFF4EEE>
This commit is contained in:
parent
2542c1f20d
commit
b5927c55d5
1 changed files with 110 additions and 6 deletions
|
|
@ -1,13 +1,17 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# evaluate-trigger.sh — Find unreviewed PRs, run 2-agent review, auto-merge if approved.
|
# evaluate-trigger.sh — Find unreviewed PRs, run 2-agent review, auto-merge if approved.
|
||||||
#
|
#
|
||||||
# Reviews each PR with TWO agents:
|
# Reviews each PR with up to THREE agents:
|
||||||
# 1. Leo (evaluator) — quality gates, cross-domain connections, coherence
|
# 1. Leo (evaluator) — quality gates, cross-domain connections, coherence
|
||||||
# 2. Domain agent — domain expertise, duplicate check, technical accuracy
|
# 2. Domain agent — domain expertise, duplicate check, technical accuracy
|
||||||
|
# 3. Ganymede (code reviewer) — code quality, correctness, safety (code PRs only)
|
||||||
#
|
#
|
||||||
# After both reviews, auto-merges if:
|
# Ganymede reviews any PR that touches code files (ops/, diagnostics/, .py, .sh, etc.)
|
||||||
|
#
|
||||||
|
# After all reviews, auto-merges if:
|
||||||
# - Leo's comment contains "**Verdict:** approve"
|
# - Leo's comment contains "**Verdict:** approve"
|
||||||
# - Domain agent's comment contains "**Verdict:** approve"
|
# - Domain agent's comment contains "**Verdict:** approve" (if applicable)
|
||||||
|
# - Ganymede's comment contains "**Verdict:** approve" (if code PR)
|
||||||
# - No territory violations (files outside proposer's domain)
|
# - No territory violations (files outside proposer's domain)
|
||||||
#
|
#
|
||||||
# Usage:
|
# Usage:
|
||||||
|
|
@ -51,6 +55,22 @@ LEO_ONLY=false
|
||||||
NO_MERGE=false
|
NO_MERGE=false
|
||||||
SPECIFIC_PR=""
|
SPECIFIC_PR=""
|
||||||
|
|
||||||
|
# --- Code PR detection ---
|
||||||
|
# Returns "true" if the PR touches code files (ops/, diagnostics/, scripts, .py, .sh, .js, .html)
|
||||||
|
# These PRs need Ganymede code review in addition to Leo's quality review.
|
||||||
|
detect_code_pr() {
|
||||||
|
local pr_number="$1"
|
||||||
|
local files
|
||||||
|
|
||||||
|
files=$(gh pr view "$pr_number" --json files --jq '.files[].path' 2>/dev/null || echo "")
|
||||||
|
|
||||||
|
if echo "$files" | grep -qE "^ops/|^diagnostics/|\.py$|\.sh$|\.js$|\.html$|\.css$|\.json$"; then
|
||||||
|
echo "true"
|
||||||
|
else
|
||||||
|
echo "false"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# --- Domain routing map ---
|
# --- Domain routing map ---
|
||||||
# Maps branch prefix or domain directory to agent name and identity path
|
# Maps branch prefix or domain directory to agent name and identity path
|
||||||
detect_domain_agent() {
|
detect_domain_agent() {
|
||||||
|
|
@ -200,7 +220,10 @@ echo "PRs to review: $PRS_TO_REVIEW"
|
||||||
if [ "$DRY_RUN" = true ]; then
|
if [ "$DRY_RUN" = true ]; then
|
||||||
for pr in $PRS_TO_REVIEW; do
|
for pr in $PRS_TO_REVIEW; do
|
||||||
read -r agent domain <<< "$(detect_domain_agent "$pr")"
|
read -r agent domain <<< "$(detect_domain_agent "$pr")"
|
||||||
echo "[DRY RUN] PR #$pr — Leo + ${agent:-unknown} (${domain:-unknown domain})"
|
is_code=$(detect_code_pr "$pr")
|
||||||
|
reviewers="Leo + ${agent:-unknown} (${domain:-unknown domain})"
|
||||||
|
[ "$is_code" = "true" ] && reviewers="$reviewers + Ganymede (code)"
|
||||||
|
echo "[DRY RUN] PR #$pr — $reviewers"
|
||||||
done
|
done
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
@ -294,6 +317,8 @@ check_merge_eligible() {
|
||||||
local pr_number="$1"
|
local pr_number="$1"
|
||||||
local domain_agent="$2"
|
local domain_agent="$2"
|
||||||
local leo_passed="$3"
|
local leo_passed="$3"
|
||||||
|
local is_code_pr="${4:-false}"
|
||||||
|
local ganymede_passed="${5:-true}"
|
||||||
|
|
||||||
# Gate 1: Leo must have completed without timeout/error
|
# Gate 1: Leo must have completed without timeout/error
|
||||||
if [ "$leo_passed" != "true" ]; then
|
if [ "$leo_passed" != "true" ]; then
|
||||||
|
|
@ -337,7 +362,29 @@ check_merge_eligible() {
|
||||||
echo "Domain agent: N/A (leo-only or grand-strategy)"
|
echo "Domain agent: N/A (leo-only or grand-strategy)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Gate 4: Territory violations
|
# Gate 4: Ganymede code review (for code PRs)
|
||||||
|
if [ "$is_code_pr" = "true" ]; then
|
||||||
|
if [ "$ganymede_passed" != "true" ]; then
|
||||||
|
echo "BLOCK: Ganymede code review failed or timed out"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local ganymede_verdict
|
||||||
|
ganymede_verdict=$(gh pr view "$pr_number" --json comments \
|
||||||
|
--jq '[.comments[] | select(.body | test("VERDICT:GANYMEDE:")) | .body] | last' 2>/dev/null || echo "")
|
||||||
|
|
||||||
|
if echo "$ganymede_verdict" | grep -q "VERDICT:GANYMEDE:APPROVE"; then
|
||||||
|
echo "Ganymede (code review): APPROVED"
|
||||||
|
elif echo "$ganymede_verdict" | grep -q "VERDICT:GANYMEDE:REQUEST_CHANGES"; then
|
||||||
|
echo "BLOCK: Ganymede requested code changes"
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
echo "BLOCK: No verdict marker found for Ganymede code review"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Gate 5: Territory violations
|
||||||
local violations
|
local violations
|
||||||
violations=$(check_territory_violations "$pr_number")
|
violations=$(check_territory_violations "$pr_number")
|
||||||
|
|
||||||
|
|
@ -475,6 +522,63 @@ Work autonomously. Do not ask for confirmation."
|
||||||
[ -n "$PR_BRANCH" ] && git branch -D "$PR_BRANCH" 2>/dev/null || true
|
[ -n "$PR_BRANCH" ] && git branch -D "$PR_BRANCH" 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# --- Review 3: Ganymede code review (for PRs touching code files) ---
|
||||||
|
IS_CODE_PR=$(detect_code_pr "$pr")
|
||||||
|
GANYMEDE_PASSED=true
|
||||||
|
|
||||||
|
if [ "$IS_CODE_PR" = "true" ] && [ "$LEO_ONLY" != true ]; then
|
||||||
|
echo " Code files detected — running Ganymede code review."
|
||||||
|
GANYMEDE_REVIEW_FILE="/tmp/ganymede-review-pr${pr}.md"
|
||||||
|
GANYMEDE_PROMPT="You are Ganymede, the code quality reviewer for the Teleo collective.
|
||||||
|
|
||||||
|
Review PR #${pr} for code quality, correctness, and safety.
|
||||||
|
|
||||||
|
First, run: gh pr view ${pr} --json title,body,files,additions,deletions
|
||||||
|
Then checkout the PR branch: gh pr checkout ${pr}
|
||||||
|
Read every changed file completely. Also read the existing versions of modified files on main for comparison.
|
||||||
|
|
||||||
|
Your review focuses on CODE QUALITY — things a code reviewer catches:
|
||||||
|
|
||||||
|
1. **Correctness** — Does the code do what it claims? Are there logic errors, off-by-one bugs, or unhandled edge cases?
|
||||||
|
2. **Safety** — Any security issues? SQL injection, path traversal, unchecked inputs, secrets in code?
|
||||||
|
3. **Breaking changes** — Does this change file formats, API responses, DB schemas, or config structures that other agents depend on? If so, is there a migration path?
|
||||||
|
4. **Error handling** — Will failures be visible or silent? Are there bare excepts, missing error messages, or swallowed exceptions?
|
||||||
|
5. **Integration** — Does the code work with the existing system? Are imports correct, paths valid, dependencies present?
|
||||||
|
6. **Simplicity** — Is this more complex than it needs to be? Could it be simpler?
|
||||||
|
|
||||||
|
Also check:
|
||||||
|
- systemd ReadWritePaths if new file write paths are introduced
|
||||||
|
- Path format consistency (absolute vs relative)
|
||||||
|
- Concurrent edit risk on shared files (app.py, bot.py, etc.)
|
||||||
|
|
||||||
|
Write your review to ${GANYMEDE_REVIEW_FILE}
|
||||||
|
|
||||||
|
CRITICAL — Verdict format: Your review MUST end with exactly one of these verdict markers (as an HTML comment on its own line):
|
||||||
|
<!-- VERDICT:GANYMEDE:APPROVE -->
|
||||||
|
<!-- VERDICT:GANYMEDE:REQUEST_CHANGES -->
|
||||||
|
|
||||||
|
Then post the review as an issue comment:
|
||||||
|
gh pr comment ${pr} --body-file ${GANYMEDE_REVIEW_FILE}
|
||||||
|
|
||||||
|
IMPORTANT: Use 'gh pr comment' NOT 'gh pr review'. We use a shared GitHub account so gh pr review --approve fails.
|
||||||
|
Sign your review as Ganymede (code reviewer).
|
||||||
|
DO NOT duplicate Leo's knowledge quality checks — he covers those. You cover code.
|
||||||
|
DO NOT merge — the orchestrator handles merge decisions after all reviews are posted.
|
||||||
|
Work autonomously. Do not ask for confirmation."
|
||||||
|
|
||||||
|
if run_agent_review "$pr" "ganymede" "$GANYMEDE_PROMPT" "sonnet"; then
|
||||||
|
GANYMEDE_PASSED=true
|
||||||
|
else
|
||||||
|
GANYMEDE_PASSED=false
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Clean up branch
|
||||||
|
git checkout main 2>/dev/null || git checkout -f main
|
||||||
|
[ -n "$PR_BRANCH" ] && git branch -D "$PR_BRANCH" 2>/dev/null || true
|
||||||
|
elif [ "$IS_CODE_PR" = "true" ] && [ "$LEO_ONLY" = true ]; then
|
||||||
|
echo " Code files detected but skipping Ganymede review (--leo-only)."
|
||||||
|
fi
|
||||||
|
|
||||||
if [ "$LEO_PASSED" = true ]; then
|
if [ "$LEO_PASSED" = true ]; then
|
||||||
REVIEWED=$((REVIEWED + 1))
|
REVIEWED=$((REVIEWED + 1))
|
||||||
else
|
else
|
||||||
|
|
@ -489,7 +593,7 @@ Work autonomously. Do not ask for confirmation."
|
||||||
else
|
else
|
||||||
echo ""
|
echo ""
|
||||||
echo " --- Merge eligibility check ---"
|
echo " --- Merge eligibility check ---"
|
||||||
MERGE_LOG=$(check_merge_eligible "$pr" "$DOMAIN_AGENT" "$LEO_PASSED")
|
MERGE_LOG=$(check_merge_eligible "$pr" "$DOMAIN_AGENT" "$LEO_PASSED" "$IS_CODE_PR" "$GANYMEDE_PASSED")
|
||||||
MERGE_RESULT=$?
|
MERGE_RESULT=$?
|
||||||
echo "$MERGE_LOG" | sed 's/^/ /'
|
echo "$MERGE_LOG" | sed 's/^/ /'
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue