Skill: Deploy
Description
Promotes a source branch into a target branch via a pull request. Creates the PR, runs tests, and merges if they pass. One approval, no extra prompts.
The deployment model is three-tier: feature branch → test → main. The invariant is that main is never pushed directly into test. If /deploy test is invoked while on main, a temporary staging branch (deploy/YYYY-MM-DD) is created from the current HEAD, the local main ref is reset to origin/main (via git branch -f, not git reset --hard), and the staging branch is deployed into test instead.
Two modes:
- •
/deploy— Promotestest→main(default). Full release with branch cleanup. - •
/deploy test— Promotes current branch →test. Integration deploy for feature branches.
Trigger
- •Invoked by the
/deployslash command. - •Source branch must be ahead of target branch.
Inputs
| Input | Type | Required | Description |
|---|---|---|---|
| target | String | No | Target branch: main (default) or test |
| tag | String | No | Version tag for the merge commit (e.g., v1.2.0). Only valid when target is main. |
Branch Resolution
| Target | Source | Condition | Use Case |
|---|---|---|---|
main | test | Always | Release: promote test to production |
test | current branch | Not on main | Integration: merge feature branch into test |
test | deploy/YYYY-MM-DD (auto-created) | On main, commits ahead of origin/main | Staging: unpushed commits deployed via temp branch |
test | (abort) | On main, clean and up-to-date | Nothing to deploy |
When target is test and you are not on main, the source is whatever branch you are on. This is typically a bean/BEAN-NNN-<slug> feature branch.
If a deploy/YYYY-MM-DD branch already exists, append -2, -3, etc. to avoid collisions.
Process
Phase 1: Preparation
- •
Save current branch — Record it so we can return at the end.
- •
Check for uncommitted changes — Run
git status --porcelain.- •If clean: continue to step 3.
- •If dirty: show the list of modified/untracked files and prompt the user:
- •Commit — Stage all changes and commit with a message summarizing the changes (follow the repo's commit style). Then continue.
- •Stash — Run
git stash --include-untracked -m "deploy-auto-stash". Restore at the end. - •Abort — Stop the deploy. The user should handle uncommitted changes manually.
- •
Determine source and target:
- •If target is
main: source =test. Checkouttest. - •If target is
testand NOT onmain: source = the saved current branch. Stay on that branch. - •If target is
testand ONmain:- •Compute staging branch name:
deploy/YYYY-MM-DD. If that already exists, append-2,-3, etc. - •Check
git log origin/main..main --oneline. If non-empty (commits ahead of origin):- •
git checkout -b <staging>— create and switch to the staging branch at current HEAD. - •
git branch -f main origin/main— update the main ref to match remote (safe because we're now on the staging branch, no working tree changes). - •source =
<staging>.
- •
- •If main matches origin/main (nothing ahead): report "Nothing to deploy — main is up-to-date with origin. Switch to a feature branch first.", restore stash, return to original branch, exit.
- •Compute staging branch name:
Note on stash interaction: Step 2 resolves dirty working tree state before step 3 runs. If the user chose "Commit", changes are now committed on main and fall into case 3.2 above (commits ahead). If the user chose "Stash", changes are held separately and not deployed — stash is restored at exit.
- •If target is
- •
Push source —
git push origin <source>to ensure remote is up to date. - •
Verify ahead of target —
git log <target>..<source> --oneline. If empty, report "Nothing to deploy", restore stash, return to original branch, exit.
Phase 2: Quality Gate
- •
Run tests —
pytest tests/on the source branch.- •If any fail: report failures, restore stash, return to original branch. Stop.
- •If all pass: record the count.
- •
Run ruff —
flake8 src/ tests/. Record result.
Phase 3: Build Release Notes
- •
Identify beans — Parse
git log <target>..<source> --onelineforBEAN-NNN:messages. Cross-reference withai/beans/_index.mdfor titles. - •
Count branches to clean (target=
mainonly) — List allbean/*branches (local + remote). Count how many are merged into main.
Phase 4: User Approval — ONE prompt
- •
Present summary and ask once:
code=================================================== DEPLOY: <source> → <target> (via PR) =================================================== Beans: <list> Tests: N passed, 0 failed Ruff: clean / N violations Post-merge: N feature branches will be deleted (branch cleanup shown only for target=main) On "go": create PR, merge it, delete branches, restore working tree. No further prompts. ===================================================
- •
Single approval:
- •Target
main: go / go with tag / abort - •Target
test: go / abort
CRITICAL: This is the ONLY user prompt. Everything after "go" runs without stopping.
- •Target
Phase 5: Execute (no further prompts)
- •
Create PR:
bashgh pr create --base <target> --head <source> \ --title "Deploy: <date> — <bean list summary>" \ --body "<release notes>"
- •
Merge PR:
bashgh pr merge <pr-number> --merge --subject "Deploy: <date> — <bean list>"
Use
--merge(not squash/rebase) to preserve history. - •
Tag (optional, target=
mainonly) — If requested:git tag <version> && git push origin --tags. - •
Delete local feature branches (target=
mainonly) — Allbean/*branches merged into main:git branch -d. Stale/orphaned ones for Done beans:git branch -D. - •
Delete remote feature branches (target=
mainonly) — Anyremotes/origin/bean/*:git push origin --delete. - •
Delete staging branch (if created) — If a
deploy/*staging branch was created in step 3:- •Delete local:
git branch -D <staging> - •Delete remote:
git push origin --delete <staging>
- •Delete local:
- •
Sync local target —
git checkout <target> && git pull origin <target>. - •
Return to original branch —
git checkout <original-branch>. If a staging branch was created (original wasmain), return tomain. - •
Restore stash — If the user chose "Stash" in step 2:
git stash pop. On conflict, prefer HEAD. (No action needed if the user chose "Commit".) - •
Report success — PR URL, merge commit, beans deployed, branches deleted (if applicable).
Key Rules
- •One approval gate. User says "go" once. Everything after is automatic.
- •Uncommitted changes prompt. If the working tree is dirty, the user chooses: commit, stash, or abort. Nothing is silently discarded.
- •PR is created AND merged. Not just created — the full cycle completes.
- •Branch cleanup only on main deploys. Feature branches are cleaned up when promoting to main, not when merging to test.
- •If a command is blocked by sandbox: print the exact command for the user to run manually, then continue with the rest.
Error Conditions
| Error | Resolution |
|---|---|
| Nothing to deploy | Report and exit |
| Tests fail | Report failures, restore stash, return. Fix first. |
| PR create fails | Report error. Check gh auth status. |
| PR merge fails | Report error. Check branch protection / conflicts. |
| User aborts | Restore stash, return to original branch |
| Command blocked | Print command for manual execution, continue |
| On main with nothing to deploy | Report and exit. Suggest switching to a feature branch. |
| Staging branch reset fails | Report error. Staging branch still holds commits for recovery. |