oh-ci
Fix CI failures on a pull request. Work in an isolated worktree, diagnose failures from check run logs, apply fixes, verify, and push.
Invocation
/oh-ci <pr-number>
<pr-number> - the pull request number with failing CI
Prerequisites
- Repo context: Run from the repo root where the PR exists
- GitHub issue PR: The PR should be from an oh-task session (branch
issue/<number>)
Flow
Load project background from AGENTS.md, relevant .oh/ artifacts, and RNA MCP context when available.
Get PR branch info and create worktree:
# Save original directory for cleanup
ORIGINAL_DIR=$(pwd)
# Get the PR branch name
BRANCH=$(gh pr view <pr-number> --json headRefName -q .headRefName)
# Fetch and create worktree tracking the remote branch
git fetch origin
git worktree add .worktrees/ci-<pr-number> -B $BRANCH origin/$BRANCH
cd .worktrees/ci-<pr-number>
Fetch CI check run details and logs:
# Get the head SHA
HEAD_SHA=$(gh pr view <pr-number> --json headRefName,commits -q '.commits[-1].oid')
# List all check runs for this commit
gh api repos/{owner}/{repo}/commits/${HEAD_SHA}/check-runs --jq '.check_runs[] | select(.conclusion == "failure") | {name: .name, id: .id, conclusion: .conclusion}'
# For each failed check run, inspect annotations separately
gh api repos/{owner}/{repo}/check-runs/{check_run_id}/annotations
# Resolve the failed workflow run non-interactively, then fetch its logs
RUN_ID=$(gh run list --commit "$HEAD_SHA" --status failure --limit 1 --json databaseId --jq '.[0].databaseId')
test -n "$RUN_ID"
gh run view "$RUN_ID" --log-failed
Diagnose failures:
- Parse the CI logs to identify the root cause
- Common categories: type errors, test failures, lint violations, build errors
- If multiple failures, identify if they share a root cause
- Read the relevant source files to understand context
Fix the code:
- Apply targeted fixes for each failure
- Stage changes (
git add)
- Run the repo-local
/review skill on staged changes
- Handle review findings:
- P1-P3 trivial: fix inline, re-stage
- P1-P3 non-trivial: create GitHub issue as descendant
- P4: discard
Verify the fix locally:
# Run the same checks that failed, if possible
# For TypeScript projects:
pnpm typecheck
pnpm test
pnpm lint
# For Rust projects:
cargo check
cargo test
cargo clippy
Adapt commands to the project's build system. Capture every result and stop before commit or push if any required check fails.
Commit fixes:
git commit -m "fix: resolve CI failures on PR #<pr-number>
- <summary of each fix>
Fixes #<descendant-issue> (if any)
[outcome:<name>]"
Push:
git push
Cleanup worktree:
cd $ORIGINAL_DIR
git worktree remove .worktrees/ci-<pr-number>
Exit and report:
- List what CI checks were failing and what was fixed
- Note any remaining issues that need human attention
- Provide PR URL
Descendant Issues
If repo-local review finds non-trivial issues during the fix, create GitHub issues:
PARENT_ISSUE=${BRANCH#issue/}
NEW_ISSUE=$(gh issue create \
--title "Fix: <brief description>" \
--body "Spawned from #${PARENT_ISSUE} during CI fix on PR #<pr-number>.
## Context
<what was found>
## Acceptance
- [ ] Fix applied
- [ ] CI passes" \
--assignee @me | grep -oE '[0-9]+$')
Complete ALL descendant issues before the final push.
Failure Modes
- Flaky tests: If a test failure appears non-deterministic, note it and push anyway. Report as "potentially flaky" in completion.
- Infrastructure failures: If CI failed due to infra (runner OOM, timeout, service outage), report as blocked — no code fix possible.
- Dependency issues: If a transitive dependency broke, attempt version pin or update. If not feasible, report as blocked.
Exit Conditions
- Success: All CI failures diagnosed and fixed, changes pushed
- Blocked: Failure requires human decision or is infrastructure-related
- Error: Cannot diagnose the failure or fix creates worse problems
Completion Signaling (MANDATORY)
CRITICAL: You MUST signal completion when done. Call the signal_completion tool as your FINAL action.
Signal based on outcome:
| Outcome |
Call |
| CI fixed, changes pushed |
signal_completion(status: "success", pr: "<pr-url>") |
| Needs human decision |
signal_completion(status: "blocked", blocker: "<reason>") |
| Unrecoverable failure |
signal_completion(status: "error", error: "<reason>") |
| If you do not signal, the orchestrator will not know you are done and the session becomes orphaned. |
|
Fallback: If the signal_completion tool is not available, output your completion status as your final message in the format: COMPLETION: status=<status> pr=<url> or COMPLETION: status=<status> error=<reason>.
1---2name: oh-ci3description: Diagnose and fix CI failures on a PR, push fixes4---56# oh-ci78Fix CI failures on a pull request. Work in an isolated worktree, diagnose failures from check run logs, apply fixes, verify, and push.910## Invocation1112`/oh-ci <pr-number>`1314- `<pr-number>` - the pull request number with failing CI1516## Prerequisites1718- **Repo context**: Run from the repo root where the PR exists19- **GitHub issue PR**: The PR should be from an oh-task session (branch `issue/<number>`)2021## Flow22231. Load project background from `AGENTS.md`, relevant `.oh/` artifacts, and RNA MCP context when available.24252. Get PR branch info and create worktree:2627 ```bash28 # Save original directory for cleanup29 ORIGINAL_DIR=$(pwd)3031 # Get the PR branch name32 BRANCH=$(gh pr view <pr-number> --json headRefName -q .headRefName)3334 # Fetch and create worktree tracking the remote branch35 git fetch origin36 git worktree add .worktrees/ci-<pr-number> -B $BRANCH origin/$BRANCH37 cd .worktrees/ci-<pr-number>38 ```39403. Fetch CI check run details and logs:4142 ```bash43 # Get the head SHA44 HEAD_SHA=$(gh pr view <pr-number> --json headRefName,commits -q '.commits[-1].oid')4546 # List all check runs for this commit47 gh api repos/{owner}/{repo}/commits/${HEAD_SHA}/check-runs --jq '.check_runs[] | select(.conclusion == "failure") | {name: .name, id: .id, conclusion: .conclusion}'4849 # For each failed check run, inspect annotations separately50 gh api repos/{owner}/{repo}/check-runs/{check_run_id}/annotations5152 # Resolve the failed workflow run non-interactively, then fetch its logs53 RUN_ID=$(gh run list --commit "$HEAD_SHA" --status failure --limit 1 --json databaseId --jq '.[0].databaseId')54 test -n "$RUN_ID"55 gh run view "$RUN_ID" --log-failed56 ```57584. Diagnose failures:59 - Parse the CI logs to identify the root cause60 - Common categories: type errors, test failures, lint violations, build errors61 - If multiple failures, identify if they share a root cause62 - Read the relevant source files to understand context63645. Fix the code:65 - Apply targeted fixes for each failure66 - Stage changes (`git add`)67 - Run the repo-local `/review` skill on staged changes68 - Handle review findings:69 - P1-P3 trivial: fix inline, re-stage70 - P1-P3 non-trivial: create GitHub issue as descendant71 - P4: discard72736. Verify the fix locally:7475 ```bash76 # Run the same checks that failed, if possible77 # For TypeScript projects:78 pnpm typecheck79 pnpm test80 pnpm lint8182 # For Rust projects:83 cargo check84 cargo test85 cargo clippy86 ```8788 Adapt commands to the project's build system. Capture every result and stop before commit or push if any required check fails.89907. Commit fixes:9192 ```bash93 git commit -m "fix: resolve CI failures on PR #<pr-number>9495 - <summary of each fix>9697 Fixes #<descendant-issue> (if any)9899 [outcome:<name>]"100 ```1011028. Push:103104 ```bash105 git push106 ```1071089. Cleanup worktree:109110 ```bash111 cd $ORIGINAL_DIR112 git worktree remove .worktrees/ci-<pr-number>113 ```11411510. Exit and report:116 - List what CI checks were failing and what was fixed117 - Note any remaining issues that need human attention118 - Provide PR URL119120## Descendant Issues121122If `repo-local review` finds non-trivial issues during the fix, create GitHub issues:123124```bash125PARENT_ISSUE=${BRANCH#issue/}126NEW_ISSUE=$(gh issue create \127 --title "Fix: <brief description>" \128 --body "Spawned from #${PARENT_ISSUE} during CI fix on PR #<pr-number>.129130## Context131<what was found>132133## Acceptance134- [ ] Fix applied135- [ ] CI passes" \136 --assignee @me | grep -oE '[0-9]+$')137```138139Complete ALL descendant issues before the final push.140141## Failure Modes142143- **Flaky tests**: If a test failure appears non-deterministic, note it and push anyway. Report as "potentially flaky" in completion.144- **Infrastructure failures**: If CI failed due to infra (runner OOM, timeout, service outage), report as blocked — no code fix possible.145- **Dependency issues**: If a transitive dependency broke, attempt version pin or update. If not feasible, report as blocked.146147## Exit Conditions148149- **Success**: All CI failures diagnosed and fixed, changes pushed150- **Blocked**: Failure requires human decision or is infrastructure-related151- **Error**: Cannot diagnose the failure or fix creates worse problems152153## Completion Signaling (MANDATORY)154155**CRITICAL: You MUST signal completion when done.** Call the `signal_completion` tool as your FINAL action.156**Signal based on outcome:**157158| Outcome | Call |159| --------- | ------ |160| CI fixed, changes pushed | `signal_completion(status: "success", pr: "<pr-url>")` |161| Needs human decision | `signal_completion(status: "blocked", blocker: "<reason>")` |162| Unrecoverable failure | `signal_completion(status: "error", error: "<reason>")` |163**If you do not signal, the orchestrator will not know you are done and the session becomes orphaned.**164165**Fallback:** If the `signal_completion` tool is not available, output your completion status as your final message in the format: `COMPLETION: status=<status> pr=<url>` or `COMPLETION: status=<status> error=<reason>`.