Skip to content

Commit 2da4ada

Browse files
ambient-code[bot]Ambient Code BotclaudeAmbient Code Bot
authored
fix: resolve stale Amber Session check runs stuck in_progress (#1255)
<!-- acp:session_id=session-44c6d156-6e05-4dd0-85c2-532a323684aa source=#1253 last_action=2026-04-10T14:30:31Z retry_count=1 --> ## Summary - **Root cause**: The `batch-pr-fixer` job created `in_progress` check runs when dispatching sessions but never updated them to `completed` when the session finished. Healthy PRs were skipped entirely without resolving their stale checks. Additionally, the `handle-comment` job always created new check runs instead of updating existing ones. - Adds `update_check_run`, `resolve_stale_checks`, and `get_existing_check_run_id` functions to the batch Python script - Resolves stale `in_progress` checks for healthy/skipped PRs on each batch cycle - Updates existing in_progress checks instead of creating duplicates (both handle-comment and batch jobs) - Polls pending sessions after dispatch (up to 30 min) and updates checks on completion - In the `handle-comment` job, finds and updates existing `in_progress` checks via PATCH instead of creating new ones via POST ## Test plan - [ ] Trigger the batch job via `workflow_dispatch` and verify stale checks on healthy PRs are completed - [ ] Comment `@ambient-code` on a PR and verify the existing in_progress check is updated (not duplicated) - [ ] Verify a new session dispatched by the batch job has its check updated to success/failure after completion Closes #1253 --- 🤖 [Ambient Session](https://ambient-code.apps.rosa.vteam-uat.0ksl.p3.openshiftapps.com/projects/ambient-platform-and-workflow-feedback-loop-running/sessions/session-44c6d156-6e05-4dd0-85c2-532a323684aa) Co-authored-by: Ambient Code Bot <bot@ambient-code.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: Ambient Code Bot <bot@ambient-code.local>
1 parent 0c5a8e2 commit 2da4ada

File tree

1 file changed

+132
-8
lines changed

1 file changed

+132
-8
lines changed

.github/workflows/amber-issue-handler.yml

Lines changed: 132 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -427,16 +427,22 @@ jobs:
427427
# Get PR head SHA
428428
HEAD_SHA=$(gh pr view ${{ steps.context.outputs.number }} --repo "${{ github.repository }}" --json headRefOid --jq '.headRefOid')
429429
430-
# Map session phase to check status/conclusion
430+
# Look for an existing in_progress "Amber Session" check run to update
431+
EXISTING_CHECK_ID=$(gh api "repos/${{ github.repository }}/commits/${HEAD_SHA}/check-runs" \
432+
--jq '.check_runs[] | select(.name == "Amber Session" and .status == "in_progress") | .id' \
433+
2>/dev/null | head -1 || echo "")
434+
435+
# Build check run arguments
431436
CHECK_ARGS=(
432-
-X POST
433437
-f "name=Amber Session"
434-
-f "head_sha=$HEAD_SHA"
435-
-f "details_url=$SESSION_URL"
436438
-f "output[title]=Amber — ${{ steps.context.outputs.prompt_type }} prompt"
437439
-f "output[summary]=Session \`$SESSION_NAME\` (phase: $SESSION_PHASE)"
438440
)
439441
442+
if [ -n "$SESSION_URL" ]; then
443+
CHECK_ARGS+=(-f "details_url=$SESSION_URL")
444+
fi
445+
440446
case "$SESSION_PHASE" in
441447
Running)
442448
CHECK_ARGS+=(-f "status=in_progress") ;;
@@ -448,8 +454,19 @@ jobs:
448454
CHECK_ARGS+=(-f "status=completed" -f "conclusion=neutral") ;;
449455
esac
450456
451-
if ! CHECK_OUTPUT=$(gh api "repos/${{ github.repository }}/check-runs" "${CHECK_ARGS[@]}" 2>&1); then
452-
echo "::warning::Failed to create check run: $CHECK_OUTPUT"
457+
if [ -n "$EXISTING_CHECK_ID" ]; then
458+
# Update the existing check run (PATCH)
459+
if ! CHECK_OUTPUT=$(gh api "repos/${{ github.repository }}/check-runs/${EXISTING_CHECK_ID}" \
460+
-X PATCH "${CHECK_ARGS[@]}" 2>&1); then
461+
echo "::warning::Failed to update check run: $CHECK_OUTPUT"
462+
fi
463+
else
464+
# Create a new check run (POST)
465+
CHECK_ARGS+=(-f "head_sha=$HEAD_SHA")
466+
if ! CHECK_OUTPUT=$(gh api "repos/${{ github.repository }}/check-runs" \
467+
-X POST "${CHECK_ARGS[@]}" 2>&1); then
468+
echo "::warning::Failed to create check run: $CHECK_OUTPUT"
469+
fi
453470
fi
454471
455472
- name: Session summary
@@ -548,13 +565,89 @@ jobs:
548565
print(f" Failed to start session: {e}")
549566
return False
550567
568+
def get_head_sha(pr_number):
569+
"""Get the head SHA of a PR."""
570+
return gh("pr", "view", str(pr_number), "--repo", REPO, "--json", "headRefOid", "--jq", ".headRefOid")
571+
572+
def get_existing_check_run_id(head_sha):
573+
"""Find an existing in_progress 'Amber Session' check run for the given SHA."""
574+
try:
575+
result = gh("api", f"repos/{REPO}/commits/{head_sha}/check-runs",
576+
"--jq", '.check_runs[] | select(.name == "Amber Session" and .status == "in_progress") | .id')
577+
ids = result.strip().split("\n") if result.strip() else []
578+
return ids[0] if ids else None
579+
except Exception:
580+
return None
581+
582+
def update_check_run(check_run_id, status, conclusion=None, title="Amber — batch fix", summary=""):
583+
"""Update an existing check run by ID."""
584+
args = ["api", f"repos/{REPO}/check-runs/{check_run_id}",
585+
"-X", "PATCH",
586+
"-f", f"status={status}",
587+
"-f", f"output[title]={title}",
588+
"-f", f"output[summary]={summary}"]
589+
if conclusion:
590+
args.extend(["-f", f"conclusion={conclusion}"])
591+
proc = subprocess.run(["gh"] + args, capture_output=True, text=True)
592+
if proc.returncode != 0:
593+
print(f" Warning: failed to update check run {check_run_id}: {proc.stderr or proc.stdout}")
594+
595+
def resolve_stale_checks(pr_number, session_name=None):
596+
"""Find all in_progress 'Amber Session' check runs on a PR and complete them."""
597+
head_sha = get_head_sha(pr_number)
598+
if not head_sha:
599+
return
600+
try:
601+
result = gh("api", f"repos/{REPO}/commits/{head_sha}/check-runs",
602+
"--jq", '.check_runs[] | select(.name == "Amber Session" and .status == "in_progress") | .id')
603+
ids = [x.strip() for x in result.strip().split("\n") if x.strip()]
604+
except Exception:
605+
ids = []
606+
if not ids:
607+
return
608+
609+
# Check session phase if we have a session name
610+
conclusion = "neutral"
611+
summary = "Session completed (resolved by batch cycle)"
612+
if session_name:
613+
phase = get_session_phase(session_name)
614+
if phase == "Completed":
615+
conclusion = "success"
616+
summary = f"Session `{session_name}` completed successfully"
617+
elif phase in ("Error", "Failed"):
618+
conclusion = "failure"
619+
summary = f"Session `{session_name}` failed (phase: {phase})"
620+
else:
621+
summary = f"Session `{session_name}` resolved (phase: {phase or 'unknown'})"
622+
623+
for check_id in ids:
624+
print(f" Resolving stale check run {check_id} on PR #{pr_number}")
625+
update_check_run(check_id, "completed", conclusion=conclusion, summary=summary)
626+
551627
def post_check_run(pr_number, session_name):
552-
"""Post an in_progress check run on the PR linking to the Amber session."""
553-
head_sha = gh("pr", "view", str(pr_number), "--repo", REPO, "--json", "headRefOid", "--jq", ".headRefOid")
628+
"""Post an in_progress check run on the PR linking to the Amber session.
629+
If an existing in_progress check exists, update it instead of creating a duplicate."""
630+
head_sha = get_head_sha(pr_number)
554631
if not head_sha:
555632
return
556633
host = os.environ.get("PLATFORM_HOST", "").rstrip("/")
557634
session_url = f"{host}/projects/{PROJECT}/sessions/{session_name}" if host else ""
635+
636+
# Resolve any existing stale checks before posting a new one
637+
existing_id = get_existing_check_run_id(head_sha)
638+
if existing_id:
639+
args = ["api", f"repos/{REPO}/check-runs/{existing_id}",
640+
"-X", "PATCH",
641+
"-f", "status=in_progress",
642+
"-f", "output[title]=Amber — batch fix",
643+
"-f", f"output[summary]=Session `{session_name}` triggered for PR #{pr_number}"]
644+
if session_url:
645+
args.extend(["-f", f"details_url={session_url}"])
646+
proc = subprocess.run(["gh"] + list(args), capture_output=True, text=True)
647+
if proc.returncode != 0:
648+
print(f" Warning: failed to update check run for PR #{pr_number}: {proc.stderr or proc.stdout}")
649+
return
650+
558651
args = ["api", f"repos/{REPO}/check-runs",
559652
"-X", "POST",
560653
"-f", "name=Amber Session",
@@ -672,6 +765,7 @@ jobs:
672765
673766
processed = 0
674767
skipped = 0
768+
pending_sessions = []
675769
676770
for pr in prs:
677771
number = pr["number"]
@@ -691,6 +785,8 @@ jobs:
691785
needs_work, reason = needs_attention(number)
692786
if not needs_work:
693787
print(f"PR #{number}: {reason}, skipping")
788+
# Resolve any stale in_progress checks on healthy PRs
789+
resolve_stale_checks(number, session_name=session_id)
694790
skipped += 1
695791
continue
696792
print(f"PR #{number}: {reason}")
@@ -750,9 +846,37 @@ jobs:
750846
result_name = create_session_api(prompt, session_name=session_id)
751847
if result_name:
752848
post_check_run(number, result_name)
849+
pending_sessions.append({"pr": number, "session": result_name})
753850
754851
processed += 1
755852
853+
# Poll pending sessions for completion and update their check runs
854+
if pending_sessions:
855+
print(f"\nPolling {len(pending_sessions)} pending session(s) for completion (max 30 min)...")
856+
poll_deadline = time.time() + 1800 # 30 minutes
857+
while pending_sessions and time.time() < poll_deadline:
858+
time.sleep(30)
859+
still_pending = []
860+
for item in pending_sessions:
861+
phase = get_session_phase(item["session"])
862+
if phase in ("Completed", "Error", "Failed", None):
863+
print(f" Session {item['session']} for PR #{item['pr']} finished (phase: {phase})")
864+
resolve_stale_checks(item["pr"], session_name=item["session"])
865+
elif phase == "Running":
866+
still_pending.append(item)
867+
else:
868+
# Unknown/stopped phase — resolve as neutral
869+
print(f" Session {item['session']} for PR #{item['pr']} in unexpected phase: {phase}")
870+
resolve_stale_checks(item["pr"], session_name=item["session"])
871+
pending_sessions = still_pending
872+
if pending_sessions:
873+
print(f" Still waiting on {len(pending_sessions)} session(s)...")
874+
875+
# Resolve any remaining sessions that timed out
876+
for item in pending_sessions:
877+
print(f" Session {item['session']} for PR #{item['pr']} timed out, resolving checks")
878+
resolve_stale_checks(item["pr"], session_name=item["session"])
879+
756880
print(f"\nBatch complete: {processed} processed, {skipped} skipped")
757881
PYEOF
758882

0 commit comments

Comments
 (0)