| name | send-pr |
| description | Prepare, validate, and send or update a pull request with full due diligence |
Prepare and send (or update) a pull request. Follow every step below in order.
If any step fails trivially (lint warning, minor test fix, formatting), fix it and continue.
If a failure is non-trivial (widespread test breakage, unclear intent), STOP and report the failure.
Step 0 — Determine GitHub username and base branch, verify commit authorship
Retrieve the GitHub username from git config:
GH_USER=$(git config github.user || gh api user -q .login)
Store this as $GH_USER — it is used for branch naming in later steps.
Determine the base branch for this PR. If a PR already exists, use its base branch. Otherwise, check if the branch was created from another feature branch (e.g. git log --oneline --graph or tracking info). Default to $BASE if unclear. Store this as $BASE — all subsequent steps that reference the upstream use $BASE instead of hardcoding $BASE.
Verify that the author and committer for every commit on this branch match the configured git identity:
EXPECTED_NAME=$(git config user.name)
EXPECTED_EMAIL=$(git config user.email)
git log $BASE..HEAD --format='%an%n%ae%n%cn%n%ce' | sort -u
Every name returned should equal $EXPECTED_NAME and every email should equal $EXPECTED_EMAIL. If any commit has a different author or committer, rewrite those commits using git rebase -x with git commit --amend --reset-author to align them with the configured identity. Do NOT use interactive rebase (-i).
Step 1 — Check for existing PR and ensure feature branch
First, check if there is an open PR for the current branch (gh pr view). Note the result — you will need it in Step 11. If a PR exists, use its base branch as $BASE.
Then ensure the branch name:
- If the current branch starts with
$GH_USER/, continue.
- If on
main/master, create a new branch named $GH_USER/<short-description> based on the recent commit messages, switch to it, and continue.
- If on another branch not prefixed with
$GH_USER/ AND there is no open PR for it, rename it with git branch -m $GH_USER/<current-name>.
- If on another branch not prefixed with
$GH_USER/ AND there IS an open PR, do not rename. Continue with the current name.
Step 2 — Commit unstaged changes
First, revert any simtest contamination: if Cargo.lock is dirty, check whether it contains simtest tokio patches (real_tokio, local path references to tokio). If so, run git checkout Cargo.lock to discard those changes. Only commit Cargo.lock changes that reflect real dependency additions from this branch.
Stage and commit all unstaged/untracked files EXCEPT:
- Markdown files that are design docs or debugging notes (not CLAUDE.md, not README.md checked into the project)
- Log files
- Tokio crate patches generated by simtest (typically in a local
patches/ dir or Cargo.toml [patch] entries pointing at local tokio paths)
All changes should go into a single commit with a concise single-line message. Do NOT include Co-Authored-By lines. Do NOT create separate commits for Cargo.lock, formatting, or other mechanical changes — everything goes into one commit that will be amended in Step 9.
If there is nothing to commit, move on.
Step 3 — Rebase on upstream
Rebase the current branch onto $BASE. Fetch first.
Resolve merge conflicts automatically — read both sides, understand intent, and pick the correct resolution. Most conflicts in this codebase are mechanical (import ordering, adjacent additions, config entries). Only stop if a conflict involves genuinely contradictory logic where the correct resolution is ambiguous.
Step 4 — Self-review
Run git diff $BASE...HEAD and review the full diff for obvious issues:
Obvious bugs & security issues - pause and alert the user
- panic, assert, unwrap, expect reachable using boundary-condition transaction inputs
- unchecked arithmetic operations with reachable overflow/underflow
- incorrect arithmetic, off-by-one errors, negation
- behavioral changes in transaction processing not gated by protocol-config causing validator equivocation
- backwards-incompatible type system changes
- any change to a bcs-encoded struct
- bcs-encoded enum variants not in last position
Obvious code quality issues:
- Leftover debug logging (
dbg!, println!, temporary info!/warn! calls, any llm signature)
todo!(), unimplemented!(), or FIXME markers
- Accidental reverts of upstream changes
- Dead code introduced by this branch
- Commented-out code that should be removed
Fix anything found. Stage changes but do not commit yet — they will be amended in Step 9. If something looks intentional but questionable, note it to the user but continue.
Step 5 — Run relevant tests
Determine which packages were changed on this branch (compare against $BASE).
- For unit tests:
SUI_SKIP_SIMTESTS=1 cargo nextest run --no-capture -p <package>
- For anything in
sui-e2e-tests: cargo simtest --no-capture -p sui-e2e-tests <test_filter>
- Consult crate-specific CLAUDE.md files for test guidance.
Fix trivial test failures (assertion message changes, snapshot updates, etc.) and re-run. Stage changes but do not commit yet — they will be amended in Step 9.
After simtest runs, ALWAYS revert Cargo.lock with git checkout Cargo.lock — simtest patches tokio locally which contaminates the lockfile with real_tokio entries that must never be committed.
If tests fail non-trivially, stop and report.
Step 6 — Clippy
Run cargo xclippy -D warnings. Fix all warnings and errors. Stage changes but do not commit yet — they will be amended in Step 9.
Repeat until clean. If a clippy issue is non-trivial to fix, stop and report.
Step 7 — Format
Run cargo fmt --all. If it produces changes, stage them but do not commit yet — they will be amended in Step 9.
Step 8 — Protocol config snapshots
If any commit on this branch touches crates/sui-protocol-config/src/lib.rs, run:
./scripts/update_all_snapshots.sh
Stage any snapshot changes but do not commit yet — they will be amended in Step 9.
Step 9 — Amend all staged changes
All changes from Steps 4–8 should now be staged but uncommitted. Before amending, run git checkout Cargo.lock one final time to ensure no simtest contamination is staged. Then amend everything into the most recent commit on the branch using git commit --amend --no-edit. The goal is a clean history: no separate commits for formatting, clippy fixes, or lockfile updates.
Step 10 — Scrub commit authorship
Check ALL commits on this branch (vs $BASE) for Co-Authored-By lines mentioning claude or anthropic (case-insensitive), as well as Generated by Claude or similar AI attribution footers. If found, use git rebase -x with git commit --amend to strip those lines. Do NOT use interactive rebase (-i).
Step 11 — Push
Push to the remote feature branch with git push -u origin HEAD. NEVER push to main or master.
If the remote branch exists and has diverged (due to rebase), force-push with git push --force-with-lease. This is expected after rebasing.
Step 12 — Open or update PR
Check if a PR already exists for this branch (gh pr view).
- If a PR is open: done. Print the PR URL.
- If no PR exists: open one in draft mode with
gh pr create --draft. Write a title and body following the description guidelines below. Print the PR URL.
Step 13 — Write the PR description
Follow the repo template (.github/PULL_REQUEST_TEMPLATE.md): ## Description, ## Test plan, ## Release notes.
Description
A description has two possible parts: the why and the what. Write the minimum that lets a reviewer approach the diff with the right context — for many PRs this is a single sentence.
- Why — the motivation, problem, or bug being addressed. Almost always include this, because it is the one thing the diff usually cannot tell the reviewer. One or two sentences. If it fixes an issue or follows up another PR, link it. If the motivation is genuinely self-evident from the title alone, keep it to a few words or omit it.
- What — what the change does. Omit this entirely when it is self-evident from the diff. Include it only when the diff is large or non-obvious, and then only as a very concise, high-level description of the mechanics — enough to orient the reviewer, not a walkthrough. Never restate at length what a reader will see immediately on opening the diff.
The test for every sentence: does it tell the reviewer something the diff does not already make obvious? If not, cut it. When in doubt, write less — a one-line description is a good outcome, not a deficiency.
NEVER do these:
- Explaining the "what" when the diff is small and self-evident. The reviewer will read the diff; do not pre-narrate it.
- Listing the files, modules, structs, or functions that were touched. The diff already shows this.
- Narrating the change blow-by-blow ("First we add X, then we modify Y to call Z..."). At most, summarize the mechanics in one high-level sentence.
- Filler adjectives: "comprehensive", "robust", "cleanly", "This PR introduces a series of changes that...". Cut them.
- Restating that the code is correct, tested, or follows best practices. Show, don't assert.
Example — a PR that tightens the PR-description rules in this very skill:
- Too verbose: a paragraph re-explaining that Claude writes verbose descriptions, what "self-evident" means, which sections were restructured, and why each rule was added.
- Right-sized: "Claude's PR descriptions are too verbose. Tighten the description rules in the send-pr skill to push for brevity." The diff shows the rest.
Test plan
Only state what a reviewer genuinely needs in order to trust the change. Do NOT restate CI blockers — "unit tests pass", "clippy clean", "cargo fmt", "CI is green" — these are assumed for every PR and add zero information.
- If new tests were added, say which behavior they cover (one line).
- If you verified something manually or by a non-obvious command, give the steps so a reviewer could repeat them.
- If the change is covered entirely by existing automated tests and there is nothing else to convey, write a single short line saying so (e.g. "Covered by existing tests in
<area>.") rather than a paragraph of boilerplate.
Release notes
Check only the boxes whose surface this PR actually changes, and for each checked box write the user-visible impact and any action users must take. Leave the rest unchecked and empty. If nothing user-facing changed, leave all boxes unchecked.
Title
One concise line describing the change in terms of its effect, matching the existing commit-message style of the repo. No type-prefix unless the surrounding history uses one.