Running in CI
First Steps — Read Context
When triggered by a comment or issue, read the full context before responding.
The prompt provides a URL — extract the PR/issue number from it.
For PRs:
bash
1gh pr view <number> --json title,body,comments,reviews,state,statusCheckRollup
2gh pr diff <number>
3gh pr checks <number>
For issues:
bash
1gh issue view <number> --json title,body,comments,state
Read the triggering comment, the PR/issue description, the diff (for PRs), and
recent comments to understand the full conversation before taking action.
Security
NEVER run commands that could expose secrets (env, printenv, set,
export, cat/echo on config files containing credentials). NEVER include
environment variables, API keys, tokens, or credentials in responses or
comments.
PR Creation
When the triggering comment asks for a PR (e.g., "make a new PR", "open a PR",
"create a PR"), create it directly with gh pr create. The comment is the
user's explicit request — don't downgrade it to a compare link.
CI Monitoring
After pushing changes to a PR branch, you must wait for CI before saying
"done" or reporting completion. A push without green CI is not finished work.
- Push your changes
- Run
gh pr checks <number> --required once
- If all required checks passed, report completion
- If checks are still running, poll with
gh pr checks <number> --required
every 60 seconds until all required checks complete (this may take up to
10 minutes). Non-required checks (e.g., benchmarks) are ignored — do not
wait for them.
- If a required check fails, diagnose with
gh run view <run-id> --log-failed,
fix issues, commit, push, and repeat from step 2
- Only after all required checks pass, report completion
Never post a "done" or "fixed" comment before CI passes. Local tests alone
are not sufficient — CI runs on Linux, Windows, and macOS. If you report
completion and CI later fails, the user has to come back and ask you to fix it
again.
Avoid gh run watch — it can hang indefinitely. Use the poll loop above
instead, which has a natural bound on CI completion time.
Prefer replying in context rather than creating a new top-level comment:
-
Inline review comments (URLs containing #discussion_r): Reply in the
review thread using gh api, not as a top-level conversation comment. Use the
review comment ID from the prompt:
bash
1cat > /tmp/reply.md << 'EOF'
2Your response here
3EOF
4gh api repos/{owner}/{repo}/pulls/{number}/comments/{comment_id}/replies \
5 -F body=@/tmp/reply.md
This keeps the discussion co-located with the code it references.
-
Conversation comments (URLs containing #issuecomment-): Post a regular
comment — GitHub doesn't support threading for these, so a new comment is
correct.
Keep comments concise. Put detailed analysis (file-by-file breakdowns, code
snippets) inside <details> tags with a short summary. The top-level comment
should be a brief overview (a few sentences); all supporting detail belongs in
collapsible sections.
Use Links
When referencing files, issues, PRs, or docs, always use markdown links so
readers can click through — never leave them as plain text.
Prefer permalinks (URLs with a commit SHA) over branch-based links
(blob/main/...). Permalinks stay valid even when files move or lines shift.
This is especially important for line references — a blob/main/...#L42 link
breaks as soon as the line numbers change. On GitHub, pressing y on any file
view copies the permalink.
- Repository files — link to the file on GitHub:
docs/content/hook.md,
not just docs/content/hook.md
- Issues and PRs — use
#123 shorthand (GitHub auto-links these)
- Specific lines — link with a line fragment:
src/cli/mod.rs#L42
- External resources — always use
[text](url) format
For file-level links, blob/main/... is acceptable since file paths are stable.
For line references, always use a permalink with a commit SHA
(blob/<sha>/...#L42) — line numbers shift frequently and branch-based line
links go stale fast.
Example:
<details><summary>Detailed findings (6 files)</summary>
...details here...
</details>
Do not add job links, branch links, or other footers at the bottom of your
comment. claude-code-action automatically adds these to the comment header.
Adding them yourself creates duplicates and broken links (the action deletes
unused branches after the run).
Shell Quoting in gh Commands
Shell expansion corrupts $ and ! in command arguments. This is a Claude
Code bug — bash history expansion mangles ! in double-quoted strings (e.g.,
format! becomes format\!) and it's the most common source
of broken bot comments.
Rule: always use a temp file for comment/reply bodies and other shell-sensitive
arguments. Never pass the body directly as a -f body="..." argument.
bash
1# Posting a comment — ALWAYS use a file
2cat > /tmp/comment.md << 'EOF'
3Fixed — the `format!` macro needed its arguments on separate lines.
4CI is now green across all platforms.
5EOF
6gh pr comment 1286 -F /tmp/comment.md
7
8# Replying to a review comment — ALWAYS use a file
9cat > /tmp/reply.md << 'EOF'
10Good catch! Updated to use `assert_eq!` instead.
11EOF
12gh api repos/{owner}/{repo}/pulls/{number}/comments/{id}/replies \
13 -F body=@/tmp/reply.md
14
15# GraphQL with $ — write query to a file, pass with -F
16cat > /tmp/query.graphql << 'GRAPHQL'
17query($owner: String!, $repo: String!) { ... }
18GRAPHQL
19gh api graphql -F query=@/tmp/query.graphql -f owner="$OWNER"
20
21# jq with ! — use a file
22cat > /tmp/jq_filter << 'EOF'
23select(.status != "COMPLETED")
24EOF
25gh api ... --jq "$(cat /tmp/jq_filter)"
Key details:
- Use
<< 'EOF' (single-quoted delimiter) to prevent all shell expansion
- Use
-F body=@/tmp/reply.md (capital -F with @ prefix) to read from file
- For
gh pr comment and gh issue comment, use -F /tmp/comment.md (the
-F flag reads body from file)
Keeping PR Titles and Descriptions Current
When you revise a PR's code in response to review feedback, check whether the
title and description still accurately describe the changes. If the approach
changed (e.g., from "exclude all X" to "add targeted exclusions for X"), update
the title and body to match. A reviewer reading the description before the diff
should not be confused by stale framing.
Use the GitHub API to update:
bash
1gh api repos/{owner}/{repo}/pulls/{number} -X PATCH \
2 -f title="new title" -F body=@/tmp/updated-body.md
Atomic PRs
When creating PRs, split unrelated changes into separate PRs — one concern per
PR. For example, a skill file fix and a workflow dependency cleanup are two
independent changes and should be two PRs, even if discovered in the same
session. This makes PRs easier to review, revert, and bisect.
A good test: if one change could be reverted without affecting the other, they
belong in separate PRs.
Investigating Other CI Runs
When asked to diagnose what a bot did in a previous CI run, the primary evidence
is the session log artifact — not the console output. Console output
(gh run view <id> --log) contains only workflow boilerplate because
show_full_output defaults to false. The actual conversation is in the
artifact.
Downloading session logs
All Claude workflows upload session logs as artifacts named
claude-session-logs. Download with:
bash
1gh run download <run-id> -n claude-session-logs -D /tmp/session-logs-<run-id>
The artifact contains JSONL files under a path like
-home-runner-work-worktrunk-worktrunk/<session-id>.jsonl.
Parsing session logs
Each JSONL line is a message with a type field (user, assistant, system).
bash
1# Skills loaded
2jq -r 'select(.type == "assistant") | .message.content[]? |
3 select(.type == "tool_use" and .name == "Skill") | .input.skill' <FILE>.jsonl
4
5# Tool calls
6jq -r 'select(.type == "assistant") | .message.content[]? |
7 select(.type == "tool_use") |
8 "\(.name): \(.input | tostring | .[0:100])"' <FILE>.jsonl
9
10# Assistant reasoning
11jq -r 'select(.type == "assistant") | .message.content[]? |
12 select(.type == "text") | .text' <FILE>.jsonl
Finding the right run
Multiple workflows may trigger on the same event. Use the event type to narrow:
bash
1gh api 'repos/{owner}/{repo}/actions/runs?per_page=30' \
2 --jq '.workflow_runs[] | select(.name | startswith("claude-")) |
3 {id, name, event, head_branch, created_at, conclusion}'
Check which runs have artifacts before downloading:
bash
1gh api repos/{owner}/{repo}/actions/runs/<run-id>/artifacts \
2 --jq '.artifacts[] | {name, size_in_bytes}'
Review-response runs triggered by pull_request_review or
pull_request_review_comment events sometimes produce no artifact when the
session is very short.
Thoroughness — Grounded Analysis
CI runs are not interactive chat. There is no back-and-forth — the user reads
your output after the session ends. Every claim must be grounded in evidence you
actually examined.
- Do the work, don't speculate. If you have access to logs, code, or API
data, read it before drawing conclusions. "This suggests X may be the cause"
is not acceptable when you can check whether X is actually the cause.
- Never claim a CI failure is "pre-existing" or "unrelated" without
evidence. Before characterizing any failure this way, check main branch CI
history (e.g.,
gh api "repos/{owner}/{repo}/actions/runs?branch=main&status=completed&per_page=5")
to verify the same test fails there. If you cannot verify, say "I haven't
confirmed whether this is pre-existing" rather than asserting it is.
- Show evidence. Cite specific log lines, file paths, commit SHAs, or API
responses. A conclusion without evidence is speculation.
- Trace causation, don't guess at correlation. If two things co-occur, find
the mechanism — don't say "this may be related."
- Distinguish what you verified from what you inferred. If you couldn't
verify something (e.g., logs weren't available), say so explicitly rather than
hedging with "may" or "suggests."
- Check artifacts, not just console logs. Console output from Claude runs is
hidden by default. Session log artifacts are the primary evidence source — see
"Investigating Other CI Runs" above.
The user can't ask follow-up questions in the same session. Treat every response
as your final answer.
Tone
You are a helpful reviewer raising observations, not a manager assigning work.
Never create checklists or task lists for the PR author. Instead, note what you
found and let the author decide what to act on.
For PR review comments on specific lines (shown as [Comment on path:line] in
<review_comments>), ALWAYS read that file and examine the code at that line
before answering. The question is about that specific code, not the PR in
general.