Ship Changes
Carry reviewed open pull requests through CI, conflicts, the merge queue, release, and a check th…
by organizzolini · MIT
The skill
Carry reviewed open pull requests through CI, conflicts, the merge queue, release, and a check that the change works on main.
Ship Changes
Begin where submit-changes ends. Take each open, implemented, reviewed pull request from "open" to "verified on main". A pull request is done when its change is observed working on main, not when it merges.
Work on one pull request at a time through steps 1–5. With several, order them by dependency: independent ones enter the queue in parallel. A pull request whose base is another branch belongs to a stack: read stacked-pull-requests.md before step 1, since stacks change steps 1–3. When a check, hook, or release run fails for a reason that is not obvious, read known-failures.md.
Watching
Wait on the host's PR monitor (mcp__ccd_pr__bind_pr, get_status) when it exists; otherwise gh run watch <id> --exit-status in the background. Never sleep-poll.
Every wait is bounded. After 3 failed attempts at the same failure, or when a step runs twice its workflow's timeout-minutes, stop and report.
Preflight
Completion: each item below holds, or is fixed and noted. Read the pull request with gh pr view <n> --json isDraft,title,body,labels,assignees,commits,reviewDecision.
- Ready for review:
isDraftis false. Continuous Integration skips drafts, so a draft shows no failures and no passes. Fix withgh pr ready <n>. - Metadata: all four description headings (🌰 Summary, 📝 Details, 🧪 Testing, 🔗 Related) carry real content; one
type:*label matching the title's type;scope:*labels exactly matching the title's scopes; onesource:*label; an assignee. Fix withgh pr edit. - Title significance: the title's type is at least as significant as the highest commit on the branch, because the squashed title alone decides the release.
- Signatures: every commit reads
verified: truefrom the API (gh api repos/<owner>/<repo>/pulls/<n>/commits --jq '.[].commit.verification.verified'), sincerequired_signaturesis a repository rule. Localgit log %G?is unreliable without the signer's public key. Unsigned commits go through sign-commits. - Review: CODEOWNERS names
@JimmyPaolinias sole owner, and GitHub asks no one to approve a pull request its sole owner authored, so an emptyreviewDecisionis fine for their own. For another author, an approval from@JimmyPaolinimust be on record, or stop and report. Never request, dismiss, or route around a review.
1. Green CI
Completion: gh pr view <n> --json mergeable,mergeStateStatus reports MERGEABLE and CLEAN, and all 11 required contexts have passed (Format, Typecheck, Secure, Test, Test Meanderaw, Test IC Suite, Guard, Build, Lint, Deprecate, Comply).
A check count proves nothing: a conflicting pull request runs no pull_request workflows, so a short green list means unvalidated. Confirm MERGEABLE first, then count the check names.
For each red check, diagnose with triage-integration, then choose a tier:
| Tier | Examples | Action |
|---|---|---|
| Fix and push | format/lint autofix; type error in a touched file; test or snapshot the change itself broke; stale generated report (callidescope, codependix, codometer); a file or function split within one project to meet a size or depth limit | Fix, run validate-code, commit with commit-code, push, resume watching |
| Raise to the user | moving, renaming, or adding modules; edits reaching a second project; dependency, lockfile, tsconfig, or limit changes; deleting or weakening a test; ADR-level or interface changes; a failure with no clear root cause | Stop. Report the failing check, root cause, and a recommended fix |
| Re-run | a flaky cause listed in known-failures.md, or a runner timeout or cancellation with no code cause | gh run rerun <id> --failed, once |
- A failing metadata check (
Validate Conventions,Comply) needs a fresh event, not a re-run, which replays the frozen payload. - Fix root causes only. Suppression comments, loosened compiler options, lint ignores, raised limits, and
--no-verifyare all off the table. - Every pushed fix gets a Conventional Commit and a signature. Mention each fix in the pull request thread so the reviewer sees what moved after approval; if a fix changes what the pull request does or its title's type, run update-pull-request.
2. Conflicts
Completion: mergeable is MERGEABLE after a push, and step 1's completion holds again.
Check mergeable after every push and whenever main moves. On CONFLICTING:
- Merge
origin/maininto the branch (in a worktree session, themcp__ccd_host__sync_with_base_branchtool), then runpnpm exec nx sync. Merge rather than rebase: a rebase flattens merge commits and breaks signatures. A stack is the exception: it needs linear history, so follow stacked-pull-requests.md. - Resolve by intent. Generated blocks (README codometer badges, callidescope and codependix reports) take either side, then regenerate with the matching
nx run <project>:<tool>:write. Hand-written code that both sides changed is a raise to the user unless the two edits are independent lines. pnpm-lock.yaml: never initialize theprojects/JimmyPaolinisubmodule; revert its lockfile entry, thenpnpm install --frozen-lockfile.- Run validate-code, commit, push, return to step 1.
3. Merge queue
Completion: the pull request's state is MERGED.
Enqueue once step 1 holds, the Preflight review condition holds, no do-not-merge label is present, and review threads are resolved:
gh pr merge <n> --squash --auto # standalone pull request
gh stack merge <top-pr> --yes --squash # stack: gh pr merge cannot merge one
A missing required review or an unresolved thread is a raise to the user; never route around it, and never pass --admin.
The queue re-runs the check workflows on a merge commit built from main plus the queued pull requests (merge_group events; minimum wait 6 minutes, response timeout 32). Watch with gh run list --event merge_group and gh pr view <n> --json state,mergeStateStatus. A pull request ejected from the queue returns to step 1 with the failing run's logs; usually main moved, so step 2 applies. Re-enqueue after the fix.
4. Deploy and release
Completion: every artifact the pull request's type entitles it to exists, or the run shows why none was expected.
Merge to main starts 🦸 Continuous Deployment (push-releases, timeout 32 minutes) and 🧑🎤 Continuous Integration. Compute the expected release from the squashed title with the Release Significance table in CONTRIBUTING.md: docs, style, test, chore, and no-release expect none.
A green run does not prove a release; read the Releases job's log and the artifacts. When a release is expected, confirm each of these:
| Artifact | Check |
|---|---|
| Codebase tag + GitHub release | gh release list --limit 3; a chore(release): 🔖 tag codebase commit on main |
| Package tags | git fetch --tags, then <project>@<version> for each IC-suite package the pull request touched |
| npm and GitHub Packages | npm view <package> version matches the tag; the GitHub Packages copy exists |
A superseded or timed-out Releases job has a known read and action under "Merge and release" in known-failures.md. Any other failure is a raise to the user with the failing step, root cause, and a fix-forward versus revert recommendation. Never revert, force-push, hand-edit versions or changelogs, or tag by hand.
5. Validate on main
Completion: evidence, recorded in the report, that the change behaves as intended on main's merge commit, not the branch.
Check out the merge commit (or install the published version) and run every check that applies:
- Repro: the scenario from the pull request's 🧪 Testing section or linked issue, which failed before and must pass now.
- Published artifacts: install each new version from a scratch directory outside the workspace and import or run it. A workspace install proves nothing about the tarball.
- Deployed environment: when the change's scope deploys (
projects/infrastructure/, an application with a chart), read projects/infrastructure/AGENTS.md and confirm the rollout and the service's behavior. - Docs, skills, and other non-code changes: every added or changed file exists on the merge commit with its merged content (
git fetch origin main, thengit show origin/main:<path>), and a skill is listed under.agents/skills/. With the check below, this is their evidence. - Post-release
main: the CI run on the tip, including the release commit, is green.
A check that cannot be run is reported as skipped with the reason, never as passed. A failed validation is a raise to the user with the evidence, and a proposed follow-up pull request.
Closeout
Completion: nothing is left claiming the work is in flight, and nothing the run created is left on the machine. Run only after step 5 passes.
- Issues: for each issue the pull request closes or advances, remove
status:in-progress, andgh issue close <n> --comment "<pull request and release>"the ones now done (a closed issue is the done signal; nostatus:donelabel exists). Close a parent spec only when every pull request under it is merged.
Local cleanup
Remove only what the shipped pull requests made or used, and only once each is MERGED. A pull request that was left undone, ejected, or raised to the user keeps its worktree, branch, and stash for the next attempt; say so in the report.
- Stack:
gh stack sync --prune. - Worktrees:
git worktree list, thengit worktree remove <path>for each shipped branch;--forceonly aftergit statusthere shows nothing left to push. In a host-managed worktree session usemcp__ccd_host__clean_up_worktreesinstead, since the session cannot delete the directory it runs in. Thengit worktree prune. - Branches: after
git fetch --prune,git branch -d <branch>for each shipped local branch. A refusal means the branch holds commitsmainlacks (a squash merge hides this): comparegit diff origin/main <branch>and report, using-Donly on an empty diff. Delete the remote branch (git push origin --delete <branch>) only when GitHub's auto-delete did not. - Stashes: the stack is shared with every other worktree and session. Drop only entries this run tagged: find them with
git stash list --format='%H %gs', thengit stash drop stash@{n}re-found by tag. Report untagged entries; never drop them. - Local files: delete scratch files, downloaded tarballs, and step 5's outside-the-workspace install directory; stop the background watchers and local servers the run started. Revert spurious tracked diffs a verification left behind (the lockfile entry) but never the committed codometer badges. Keep gitignored files the tooling needs (
node_modules, the emitted conformetry plugin,.nxcache). - Main: last,
git fetch origin main, even when step 5 already did, since a release commit may have landed since. Fast-forward a checked-out localmainwithgit merge --ff-only origin/main(never a plain pull or a rebase); report rather than force it if that refuses or the checkout has uncommitted work. - Check:
git worktree list,git branch --list,git stash list, andgit status --shortin the main checkout show only what predates the run.
Report
End with one table row per pull request: number, outcome, release (or "none expected"), validation evidence. Then list anything left undone and why, including any worktree, branch, or stash kept on purpose.
Use this skill
Connect your agent to the Skill Store MCP server and ask for it by ref: organizzolini/ship-changes-81568859373.
Details
- Publisher
- organizzolini
- Category
- Software Development › Source control & code review
- License
- MIT
- Updated
- 2026-10-19
References
More from organizzolini
- Agent Session Hooks Explains how startup scripts run at the start of agent sessions and inject context.
- Ask Matt Ask which skill or flow fits your situation.
- Backup Code Create a safety backup before potentially destructive actions.
- Brainstorming You MUST use this before any creative work - creating features, building components, adding funct…