Agent skill
running-in-ci
Generic CI environment rules for GitHub Actions workflows. Use when operating in CI — covers security, CI monitoring, comment formatting, and investigating session logs from other runs.
Install this agent skill to your Project
npx add-skill https://github.com/max-sixty/tend/tree/main/plugins/tend-ci-runner/skills/running-in-ci
Metadata
Additional technical details for this skill
- internal
- YES
SKILL.md
Running in CI
First Steps — Load Repo-Specific Guidance
Most repos have a project-specific overlay skill (typically running-tend) with project-specific
CI context — which workflows tend-ci-fix watches, PR title conventions, label policies. If a
running-tend skill is listed in your available skills, load it with the Skill tool before doing
anything else.
Repo-local skills are invoked by their unprefixed name — Skill: running-tend, not
Skill: tend-ci-runner:running-tend (that prefix is reserved for this plugin's own skills, and
trying it returns Unknown skill).
Repo-local skills must have YAML frontmatter (name + description) to be auto-discovered.
If you are going to propose a code fix for a bug, load /tend-ci-runner:triage first — it
contains reproduction and testing gates that apply to all fix attempts, not just initial triage.
Conduct
Follow the project's code of conduct. Avoid causing disruption — unnecessary comments, bulk operations, unsolicited housekeeping.
Helping vs. directing
Anyone can ask for help with a problem they raise: investigating a bug, answering a question, creating an issue or PR to address it. These are proposals — a maintainer still decides what to merge or act on.
Directing the bot to affect someone else's work — closing, reopening, or locking issues/PRs,
dismissing reviews, reverting commits, applying or removing labels — requires maintainer access. Before
complying, check the requester's author_association via the event payload or API:
gh api repos/{owner}/{repo}/issues/comments/{comment_id} --jq '.author_association'
OWNER, MEMBER, and COLLABORATOR indicate maintainer access. For anyone else, briefly explain
that a maintainer needs to make that call.
The test: "Am I helping this person with something they raised, or following a directive that affects someone else's work?"
Repo-specific guidance (loaded via running-tend or equivalent) always takes precedence over these
defaults. If a repo's skill explicitly authorizes an action (e.g., closing duplicate issues during
triage), follow the repo-specific instruction.
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:
gh pr view <number> --json title,body,comments,reviews,state,statusCheckRollup
gh pr diff <number>
gh pr checks <number>
For issues:
gh 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.
Restrictions
- Secrets: Never run commands that expose secrets (
env,printenv,set,export,cat/echoon credential files). Never include tokens or credentials in responses or comments. - Merging: Never merge PRs or enable auto-merge (
gh pr merge,gh pr merge --auto). PRs are proposals — a maintainer decides when to merge. - Scope: Do not create issues, PRs, or comments in repositories outside the organization, unless the target repo explicitly welcomes AI-created issues (e.g., in its CONTRIBUTING guide).
- Hanging commands: Never use
gh run watchorgh pr checks --watch— both hang indefinitely. Poll withgh pr checksin a loop instead.
PR Creation
When asked to create a PR, use gh pr create directly.
Before creating a branch or PR, check for existing work:
gh pr list --state open --json number,title,headRefName --jq '.[] | "#\(.number) [\(.headRefName)]: \(.title)"'
git branch -r --list 'origin/fix/*'
If an existing PR addresses the same problem, work on that PR instead.
Pushing to PR Branches
Always use git push without specifying a remote — gh pr checkout configures tracking to the
correct remote, including for fork PRs. Specifying origin explicitly can push to the wrong place.
If pushing fails (fork PR with edits disabled), fall back to posting code snippets in a comment. Don't reference commit SHAs from temporary branches — post code inline.
Merging Upstream into PR Branches
When asked to merge the default branch into a PR branch:
-
Never use
--allow-unrelated-histories. Ifgit mergefails because git can't find a merge base, the checkout is broken — investigate rather than forcing the merge.--allow-unrelated-historiestreats both sides as disconnected, creating add/add conflicts in every file. -
Handle untracked file conflicts properly. If
git merge origin/mainfails because untracked files would be overwritten by tracked files, stash them first — don't delete them:bashgit stash --include-untracked git merge origin/main git stash pop -
Verify merge base exists before merging:
bashgit merge-base origin/main HEADIf this fails, the branch history is disconnected. Re-checkout the PR with full history (
fetch-depth: 0) before retrying.
CI Monitoring
After pushing, wait for CI before reporting completion.
Use run_in_background: true for the polling loop so it does not block the session. When the
background task completes you will be notified — check the result and take any follow-up action
(dismiss approval, post analysis) at that point.
# Run with Bash tool's run_in_background: true.
# Filter out the current run ($GITHUB_RUN_ID) — its own job check will always
# show as "pending" since it IS the running job. Watching yourself deadlocks.
# Match on the run URL, not the check name: `gh pr checks` shows the job name
# (e.g. "review"), which does not match $GITHUB_WORKFLOW ("tend-review").
# Use `||` rather than if-based negation. The Bash tool escapes the
# exclamation mark to a literal backslash-exclamation, which prevents bash
# from recognizing the pipeline-negation reserved word and leaves the loop
# stuck until the 10-minute timeout.
for i in $(seq 1 10); do
sleep 60
gh pr checks <number> --required 2>&1 | grep -v "/runs/$GITHUB_RUN_ID/" | grep -q 'pending\|queued\|in_progress' || {
gh pr checks <number> --required
exit 0
}
done
echo "CI still running after 10 minutes"
exit 1
- Poll
gh pr checks <number> --requiredevery 60 seconds until all required checks complete (up to ~10 minutes). Ignore non-required checks (benchmarks). Filter out the current run's URL (/runs/$GITHUB_RUN_ID/) — the current workflow's own check is always pending while polling and must be excluded to avoid a deadlock. - If a required check fails, diagnose with
gh run view <run-id> --log-failed, fix, commit, push, repeat. - Report completion only after all required checks pass.
Before dismissing local test failures as "pre-existing", check main branch CI:
gh api "repos/{owner}/{repo}/actions/runs?branch=main&status=completed&per_page=3" \
--jq '.workflow_runs[] | {conclusion, created_at: .created_at}'
If you cannot verify, say "I haven't confirmed whether these failures are pre-existing."
Replying to Comments
Reply in context rather than creating new top-level comments:
-
Inline review comments (
#discussion_r): To read a single review comment, use the comment ID without the PR number in the path:bashgh api repos/{owner}/{repo}/pulls/comments/{comment_id}To reply:
bashcat > /tmp/reply.md << 'EOF' Your response here EOF gh api repos/{owner}/{repo}/pulls/{number}/comments/{comment_id}/replies \ -F body=@/tmp/reply.md -
Review events with inline comments (review ID in prompt): A review may include inline comments. Fetch them by review ID and reply to each individually:
bashgh api repos/{owner}/{repo}/pulls/{number}/reviews/{review_id}/comments \ --jq '.[] | {id: .id, path: .path, body: .body}'Reply to each comment using the inline review comment reply endpoint above.
-
Conversation comments (
#issuecomment-): Post a regular comment (GitHub doesn't support threading).
Multi-way Conversations
Before responding, check how many distinct other participants are in the conversation.
- Two-party (you and one other participant): respond normally.
- Multi-way (multiple other participants): apply a stricter bar — only respond with concrete new information no one else provided: a code fix, reproduction, or specific technical detail.
Do not:
- Restate, agree with, or summarize what another participant just said
- Post "makes sense" or "good point" agreement comments
- Echo a user's findings back to them ("Good find!", "That's the smoking gun!")
A comment that responds to concerns you raised in a review is directed at you — briefly acknowledge resolution or explain why concerns remain.
If a maintainer has already addressed the point, exit silently unless you can add something they missed.
Self-conversation Guard
If you are responding to your own prior comment or review (not a human's reply to it), only respond if there is a distinct role boundary (e.g., you are the reviewer on your own PR and need to address review feedback). If there is no such role distinction, exit silently to avoid self-conversation loops.
Recheck Before Posting
Before posting any comment or review, re-fetch the current conversation state. Other participants may comment while you work — even in short sessions, context can change between when you read a thread and when you reply:
# For issues
gh issue view <number> --json comments --jq '.comments | length'
# For PRs (comments + reviews)
gh pr view <number> --json comments,reviews \
--jq '{comments: (.comments | length), reviews: (.reviews | length)}'
Compare with the count you saw when you first read the context. If new comments or reviews appeared:
- Read the new comments to understand what changed.
- Adjust or skip your response. If someone already answered, don't repeat them. If the author resolved the issue, acknowledge that instead of posting a stale analysis. If new information contradicts your findings, update your response before posting.
- If your response is now entirely redundant, don't post it.
Dedup check for inline review comment replies
A single PR review can fire both pull_request_review and pull_request_review_comment events,
triggering separate workflow runs (serialized by the concurrency group, not truly concurrent).
Before replying to an inline review comment, check whether the bot already replied:
BOT_LOGIN=$(gh api user --jq '.login')
EXISTING=$(gh api "repos/{owner}/{repo}/pulls/{number}/comments?per_page=100" \
--jq "[.[] | select(.in_reply_to_id == {comment_id} and .user.login == \"$BOT_LOGIN\")] | length")
If EXISTING is greater than 0, do not post — another run already handled this comment. Exit
silently.
Comment Formatting
Line wrapping: GitHub renders newlines literally in issue bodies, PR descriptions, and
comments — a line break in the source becomes a <br> in the output. Write each paragraph as a
single long line and let the browser reflow. Hard wraps at 72–80 chars create awkward mid-sentence
breaks on GitHub. (This applies to GitHub-rendered content only, not to skill files or code.)
Keep comments concise. Put supporting detail inside <details> tags — the reader should get the
gist without expanding. Don't collapse content that is the answer (e.g., a requested analysis).
<details><summary>Detailed findings (6 files)</summary>
...details here...
</details>
Always use markdown links for files, issues, PRs, and docs. Any link containing #L must
use a commit SHA, never blob/main/...#L42 — line numbers shift silently, so the link stays
valid but starts pointing at different code than the comment describes. Get the SHA with
git rev-parse HEAD before composing the link.
GitHub URLs — always embed $GITHUB_REPOSITORY. Construct links as
https://github.com/${GITHUB_REPOSITORY}/...; never hand-type the owner. The model reliably
guesses wrong — past comments have shipped with anthropics/worktrunk and worktrunk/worktrunk
on a repo actually owned by max-sixty. Before posting a comment, scan it for github.com/ and
confirm every owner matches $GITHUB_REPOSITORY. Also check that the variable actually
expanded — if you see a literal ${GITHUB_REPOSITORY} in the rendered comment, you used a
single-quoted heredoc (<< 'EOF') which disables expansion. Rewrite using an unquoted <<EOF
(so ${GITHUB_REPOSITORY} interpolates) or compose the body with the Write tool.
Bash heredocs are also a trap for comment bodies containing exclamation marks — the Bash tool
rewrites every exclamation mark to a literal backslash-bang before bash parses the heredoc, so
a greeting like "Thanks for the suggestion!" renders as "Thanks for the suggestion!" in the
posted comment. The quoting mode doesn't matter: both << 'EOF' and <<EOF lose the
character. Use the Write tool for any comment body containing an exclamation mark, then pass
the file to gh ... --body-file.
- File-level link (no
#Lanchor):blob/main/src/foo.rsis fine - Line reference:
blob/<sha>/src/foo.rs#L42— commit SHA required, neverblob/main/...#L42 - Issues/PRs:
#123shorthand - External:
[text](url)format
Don't add job links or footers — claude-code-action adds these automatically.
Keeping PR Titles and Descriptions Current
When revising code after review feedback, update the title and description if the approach changed:
gh api repos/{owner}/{repo}/pulls/{number} -X PATCH \
-f title="new title" -F body=@/tmp/updated-body.md
Atomic PRs
Split unrelated changes into separate PRs — one concern per PR. If one change could be reverted without affecting the other, they belong in separate PRs.
Investigating Other CI Runs
Load /install-tend:debug-ci-session for session log download, JSONL parsing queries, and
diagnostic workflow. The primary evidence for diagnosing bot behavior is the session log
artifact — not console output.
Review-response runs triggered by pull_request_review or pull_request_review_comment events
sometimes produce no artifact when the session is very short.
Grounded Analysis
CI runs are not interactive — every claim must be grounded in evidence. The user can't ask follow-up questions; treat every response as your final answer.
Read logs, code, and API data before drawing conclusions. Show evidence: cite log lines, file paths, commit SHAs. Trace causation — if two things co-occur, find the mechanism rather than saying "this may be related." Never claim a failure is "pre-existing" without checking main branch CI history. Distinguish what you verified from what you inferred.
User-facing comments require source evidence
Public comments — on issues, PRs, or in review threads — are permanent and visible. A hallucinated detail (wrong syntax, invented API, nonexistent flag) misleads users and erodes trust. It is always better to take longer and produce a correct response than to respond quickly with fabricated details.
Before posting any specific claim — a configuration snippet, command syntax, variable name, or API behavior — find the source text that confirms it. Source text means documentation, help output, test expectations, or the code that implements the public interface. Internal implementation code (struct fields, variable names in Rust/Python/etc.) shows what exists internally but not how it's exposed to users — read the docs or user-facing layer too.
Bad: Saw extra_vars.push(("target", target_branch)) in Rust source → posted a hook example
using $WT_TARGET (an environment variable that doesn't exist — hooks use {{ target }} Jinja
templates).
Good: Saw ("target", target_branch) in Rust source → read docs/hook.md → confirmed hooks use
{{ target }} syntax → posted correct example.
For behavioral claims — "X happens when you run Y", "command Z works in scenario W" — reading code is not sufficient. Code has conditional branches, early returns, and error paths that are easy to miss when tracing mentally. Before asserting what a command does in a specific scenario, either find a test that exercises that exact scenario or run the command yourself. If neither is feasible, hedge: "Based on code reading, I believe X, but I haven't verified this end-to-end."
Bad: Read CommandEnv::for_action("commit", config) → saw it constructs an env → concluded
wt step commit works in a detached worktree. Missed that for_action() calls
require_current_branch(), which errors on detached HEAD.
Good: Read for_action() → noticed it calls require_current_branch() → uncertain whether
detached HEAD hits that path → ran cargo build && wt step commit in a detached worktree →
confirmed the error → posted accurate answer.
When a project has user-facing documentation (a docs site, --help pages, a wiki), link to it. A
link to the relevant docs page is more useful than a paraphrased explanation — and finding the link
forces verifying the claim.
If you can't find source evidence for a specific detail, say so ("I'm not sure of the exact syntax") rather than guessing. An honest gap is fixable; a confident hallucination gets copy-pasted.
Rewriting is authoring
Cross-posting, summarizing, or paraphrasing is not copying — any new content you add requires the same source verification as a fresh comment. If you expand with a config section header, code block, or usage example, verify each addition against the source.
Bad: Cross-posted a hook snippet, added an alias example with [step] as the section header
(inferred from the wt step command name). The actual config section is [aliases].
Good: Cross-posted a hook snippet, added an alias example → checked dev/*.example.toml to
confirm the section is [aliases] → posted with correct syntax.
Tone
Raise observations, don't assign work. Never create checklists or task lists for the PR author.
PR Review Comments
For review comments on specific lines ([Comment on path:line]), read that file and examine the
code at that line before answering.
When the GitHub API returns a diff_hunk, the reviewer's comment targets the last line of that
hunk. Use this to disambiguate when multiple candidates exist nearby — match the reviewer's request
against the specific anchored line, not the surrounding region.
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
release
Tend release workflow. Use when user asks to "do a release", "release a new version", "cut a release", or wants to publish a new version to PyPI.
running-tend
Tend-specific guidance for tend CI workflows. Adds non-standard workflow inclusion for usage analysis and repo conventions on top of the generic tend-* skills.
weekly
Weekly maintenance — reviews dependency PRs.
triage
Triages new GitHub issues — classifies, reproduces bugs, attempts conservative fixes, and comments. Use when a new issue is opened and needs automated triage.
nightly
Nightly code quality sweep — resolves bot PR conflicts, reviews recent commits, surveys existing code, checks resolved issues, and updates tend workflows.
ci-fix
Debug and fix failing CI on the default branch. Use when CI fails on main.
Didn't find tool you were looking for?