skills/ship/SKILL.md
--- name: ship description: Ship a feature branch the local-CI-first way — run the full local gate, push, open a PR, squash-merge, then deploy, without waiting on GitHub Actions. Use when a branch is ready for main and you want it merged and deployed now. Reads CI policy from `ro ci` (default skips remote CI because GitHub Actions billing keeps hitting limits). Sibling to /ro:gh-ship (waits on GitHub checks) and /ro:cf-ship (the deploy half). Triggers on "ship it", "ship this", "merge and deploy
npx skillsauth add RonanCodes/ronan-skills skills/shipInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
The premise: GitHub Actions billing keeps hitting spending limits, so waiting on remote CI stalls every merge. We don't want to wait on it. Instead the local git hooks are the real gate (pre-commit formats, pre-push runs the full local CI), and once the local gate is green we open the PR and squash-merge straight away, then deploy. GitHub still records the PR; it just isn't the gate.
This is the default. A project can opt back into waiting on remote CI by setting
remoteCI: "require" (see Policy below), in which case this skill behaves
like /ro:gh-ship.
main. Always a feature branch → PR → squash-merge.main.autoDeploy: false or --no-deploy).Read it, don't hardcode it:
ro ci show # full resolved policy
ro ci get remoteCI # "skip" | "require"
ro ci get mergeOnLocalPass # bool
ro ci get autoDeploy # bool
Resolution order (later wins): built-in default → ~/.claude/ci-policy.json
(global) → .ro-ci.json (committed, repo-declared) → .ro-ci.local.json
(gitignored, personal override). Set the global once with ro ci init then edit.
--require-remote-ci forces remoteCI=require for this run; --no-deploy
forces autoDeploy=false.
1. Guard: refuse if on main/master. If on main, stop and tell the user to branch.
2. Ensure work is committed (defer to /ro:commit for message + timestamp rules).
3. Run the local gate explicitly so failures surface here, not just in the hook:
pnpm quality-checks (or the repo's equivalent; see "Local gate" below)
If it fails, stop. Do not push.
4. Push the branch (the pre-push hook re-runs the gate — that's fine, it's fast).
5. Open the PR: gh pr create --base main --fill (or --title from arg)
6. Merge, per policy:
remoteCI == "skip" and mergeOnLocalPass:
gh pr merge --squash --admin --delete-branch
(--admin bypasses the billing-blocked / unstarted remote checks)
remoteCI == "require":
watch checks, merge on green — hand off to /ro:gh-ship behaviour.
7. Deploy, if autoDeploy and not --no-deploy:
defer to the repo's deploy path — /ro:cf-ship (Cloudflare),
/ro:fly-deploy (Fly), or `pnpm deploy`. Skip for non-deployable repos
(skills, wiki) — those have no deploy step.
8. Sync local main: git checkout main && git pull --ff-only
If --admin is refused (not an admin on the repo), fall back to: report that the
merge needs either admin rights or remote CI, and ask the user how to proceed.
The gate is whatever the repo's pre-push hook runs. For the standard pnpm repo
that's the quality-checks script:
prettier --check . && eslint && astro check && astro build && vitest run
Plus Playwright e2e where it stays fast. Perf budget: the whole gate should finish in a couple of minutes. If it creeps past ~3–4 min, investigate and trim (Docker-in-the-loop and slow full-browser matrices are the usual culprits) rather than living with a gate nobody waits for. A gate that's too slow gets skipped, which defeats the point.
Apply this set to any repo following the policy (see /ro:git-guardrails for the guard details):
| Hook | Runs | Why |
|---|---|---|
| pre-commit | lint-staged (prettier --write on staged) | never commit unformatted code |
| commit-msg | commitlint --edit "$1" | enforce emoji-conventional commits |
| pre-push | full local CI + refuse pushes to main | the real gate; protect main |
The pre-push main-guard:
while read -r _ _ remote_ref _; do
case "$remote_ref" in refs/heads/main|refs/heads/master)
echo "✋ Direct push to main is not allowed. Open a PR and squash-merge." >&2
exit 1 ;;
esac
done
Belt-and-braces: also set GitHub branch protection requiring a PR
(gh api -X PUT repos/{owner}/{repo}/branches/main/protection ...) so main is
protected even when hooks aren't installed. Caveat: branch protection needs
GitHub Pro for private repos (free returns "Upgrade to GitHub Pro or make this
repository public"). For private repos on the free plan, the pre-push main-guard
is the only enforcement — make sure the hook is installed.
development
--- name: worktree description: Coordinate multiple agents on one repo via a worktree-lock pool, so two agents never clobber each other's working tree. Acquire the first free slot (main, then beta/gamma… worktrees, created on demand), work there on your own branch, release when you've pushed. Use before modifying any repo that might be in use by another agent (factory, dataforce, etc.), or whenever you're told a repo is being worked on. Backed by `ro worktree`. category: development argument-hin
testing
--- name: setup-logging description: Set up (or audit) the observability stack in a TanStack Start + Cloudflare Workers app so it is "diagnosable by default" — structured logging (logtape) with a request context carrying trace_id + userId + tenant/orgId, a trace_id propagated FE→BE→logs→Sentry→PostHog, Cloudflare Workers observability enabled, and Sentry + PostHog wired. Two modes: `setup` (wire it into an app) and `audit` (check an existing app + report gaps). Use when scaffolding a new app, wh
development
Manage credentials INSIDE the active ~/.claude/.env file — read which token/account to use for a given app (Simplicity vs Dataforce vs Ronan-personal), add or update a secret WITHOUT it passing through the chat (an interactive Terminal window prompts for it), and track secrets that were exposed in a transcript so they get rotated. Sibling to /ro:context (which switches WHICH env file is active). Use when the user wants to add an API key/token/secret, asks "which credential do I use for X", needs the env organized/labelled, or a secret was pasted into the chat and should be rotated.
development
Diagnose a runtime bug by sweeping the observability logs (Cloudflare Workers logs, Sentry, PostHog) using the shared trace_id, and if the logs don't capture what's needed, ADD structured logging at the failure point + in the request context, deploy (remote) or run locally, and have the user reproduce. Use whenever the user mentions logs, a bug, an issue, "why did X fail / not work", "check the logs", "it works on desktop but not mobile", a stack trace, an error code, or a flaky/intermittent failure. Auto-pick this before guessing at a fix.