feat: add /speckit.ship command — release engineering automation#2045
feat: add /speckit.ship command — release engineering automation#2045arunt14 wants to merge 3 commits intogithub:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a new /speckit.ship core command template and a corresponding release/PR output template to automate a spec-driven “ship” workflow (readiness checks → branch sync → changelog/PR creation → archival), including extension hook integration points.
Changes:
- Add
templates/commands/ship.mdcommand template describing the end-to-end release engineering workflow (withbefore_ship/after_shiphook handling). - Add
templates/ship-template.mdas the structured release record + PR description scaffold to be written under a feature’sreleases/directory.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| templates/commands/ship.md | New /speckit.ship command template outlining readiness checks, branch sync, CI verification, PR creation, and artifact archival. |
| templates/ship-template.md | New markdown template for release notes + traceability links to spec/plan/tasks/review/QA artifacts. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
templates/commands/ship.md
Outdated
| - Fetch latest from remote: `git fetch origin` | ||
| - Check if feature branch is behind target branch: | ||
| ```bash | ||
| git rev-list --count HEAD..origin/{target_branch} | ||
| ``` | ||
| - If behind, offer to rebase or merge: | ||
| - **Rebase** (recommended for clean history): `git rebase origin/{target_branch}` | ||
| - **Merge**: `git merge origin/{target_branch}` | ||
| - If conflicts arise: **STOP** and provide conflict resolution guidance | ||
| - After sync, push the updated feature branch: `git push origin {feature_branch}` | ||
|
|
||
| 5. **Changelog Generation**: | ||
| - Collect changelog inputs: | ||
| - Feature summary from `spec.md` (overview section) | ||
| - Implementation highlights from completed tasks in `tasks.md` | ||
| - Git commit messages: `git log origin/{target_branch}..HEAD --oneline` |
There was a problem hiding this comment.
Step 3 says to detect the remote name (defaulting to origin), but branch sync hard-codes origin (git fetch origin). This will fail for repos whose primary remote isn't named origin (fork workflows, corporate defaults). Use the detected remote consistently in the fetch/compare/push commands.
| - Fetch latest from remote: `git fetch origin` | |
| - Check if feature branch is behind target branch: | |
| ```bash | |
| git rev-list --count HEAD..origin/{target_branch} | |
| ``` | |
| - If behind, offer to rebase or merge: | |
| - **Rebase** (recommended for clean history): `git rebase origin/{target_branch}` | |
| - **Merge**: `git merge origin/{target_branch}` | |
| - If conflicts arise: **STOP** and provide conflict resolution guidance | |
| - After sync, push the updated feature branch: `git push origin {feature_branch}` | |
| 5. **Changelog Generation**: | |
| - Collect changelog inputs: | |
| - Feature summary from `spec.md` (overview section) | |
| - Implementation highlights from completed tasks in `tasks.md` | |
| - Git commit messages: `git log origin/{target_branch}..HEAD --oneline` | |
| - Fetch latest from remote: `git fetch {remote_name}` | |
| - Check if feature branch is behind target branch: | |
| ```bash | |
| git rev-list --count HEAD..{remote_name}/{target_branch} | |
| ``` | |
| - If behind, offer to rebase or merge: | |
| - **Rebase** (recommended for clean history): `git rebase {remote_name}/{target_branch}` | |
| - **Merge**: `git merge {remote_name}/{target_branch}` | |
| - If conflicts arise: **STOP** and provide conflict resolution guidance | |
| - After sync, push the updated feature branch: `git push {remote_name} {feature_branch}` | |
| 5. **Changelog Generation**: | |
| - Collect changelog inputs: | |
| - Feature summary from `spec.md` (overview section) | |
| - Implementation highlights from completed tasks in `tasks.md` | |
| - Git commit messages: `git log {remote_name}/{target_branch}..HEAD --oneline` |
templates/commands/ship.md
Outdated
| - Fetch latest from remote: `git fetch origin` | ||
| - Check if feature branch is behind target branch: | ||
| ```bash | ||
| git rev-list --count HEAD..origin/{target_branch} | ||
| ``` | ||
| - If behind, offer to rebase or merge: | ||
| - **Rebase** (recommended for clean history): `git rebase origin/{target_branch}` | ||
| - **Merge**: `git merge origin/{target_branch}` | ||
| - If conflicts arise: **STOP** and provide conflict resolution guidance | ||
| - After sync, push the updated feature branch: `git push origin {feature_branch}` | ||
|
|
||
| 5. **Changelog Generation**: | ||
| - Collect changelog inputs: | ||
| - Feature summary from `spec.md` (overview section) | ||
| - Implementation highlights from completed tasks in `tasks.md` | ||
| - Git commit messages: `git log origin/{target_branch}..HEAD --oneline` |
There was a problem hiding this comment.
Branch sync continues to hard-code origin in the behind-check and push steps (origin/{target_branch} and git push origin ...). If the remote name is detected earlier, these should use that value as well so the command works in non-standard remote setups.
| - Fetch latest from remote: `git fetch origin` | |
| - Check if feature branch is behind target branch: | |
| ```bash | |
| git rev-list --count HEAD..origin/{target_branch} | |
| ``` | |
| - If behind, offer to rebase or merge: | |
| - **Rebase** (recommended for clean history): `git rebase origin/{target_branch}` | |
| - **Merge**: `git merge origin/{target_branch}` | |
| - If conflicts arise: **STOP** and provide conflict resolution guidance | |
| - After sync, push the updated feature branch: `git push origin {feature_branch}` | |
| 5. **Changelog Generation**: | |
| - Collect changelog inputs: | |
| - Feature summary from `spec.md` (overview section) | |
| - Implementation highlights from completed tasks in `tasks.md` | |
| - Git commit messages: `git log origin/{target_branch}..HEAD --oneline` | |
| - Fetch latest from remote: `git fetch {remote_name}` | |
| - Check if feature branch is behind target branch: | |
| ```bash | |
| git rev-list --count HEAD..{remote_name}/{target_branch} | |
| ``` | |
| - If behind, offer to rebase or merge: | |
| - **Rebase** (recommended for clean history): `git rebase {remote_name}/{target_branch}` | |
| - **Merge**: `git merge {remote_name}/{target_branch}` | |
| - If conflicts arise: **STOP** and provide conflict resolution guidance | |
| - After sync, push the updated feature branch: `git push {remote_name} {feature_branch}` | |
| 5. **Changelog Generation**: | |
| - Collect changelog inputs: | |
| - Feature summary from `spec.md` (overview section) | |
| - Implementation highlights from completed tasks in `tasks.md` | |
| - Git commit messages: `git log {remote_name}/{target_branch}..HEAD --oneline` |
templates/commands/ship.md
Outdated
|
|
||
| ## Operating Constraints | ||
|
|
||
| **SAFE BY DEFAULT**: Every destructive operation (force push, branch delete, PR creation) requires explicit user confirmation. Default to dry-run mode for destructive git operations. |
There was a problem hiding this comment.
The template states "SAFE BY DEFAULT" and that destructive operations require explicit user confirmation, but the branch sync flow instructs a rebase/merge and then a push without an explicit confirm gate immediately before those actions. Add a required confirmation (default "no") before rebase/merge, and another before pushing, since both can change history and/or update the remote.
| **SAFE BY DEFAULT**: Every destructive operation (force push, branch delete, PR creation) requires explicit user confirmation. Default to dry-run mode for destructive git operations. | |
| **SAFE BY DEFAULT**: Every destructive or potentially destructive operation (e.g., rebase/merge during branch sync, push/force push, branch delete, PR creation) requires explicit user confirmation. | |
| - For the branch sync flow, add a required confirmation prompt **immediately before** performing any rebase/merge, with the default answer set to **no**. | |
| - Add a separate required confirmation prompt **immediately before** performing any push (including force push) to the remote, with the default answer set to **no**. | |
| - Default to dry-run mode for destructive git operations wherever possible. |
| ```bash | ||
| gh pr create --base {target_branch} --head {feature_branch} --title "{PR title}" --body-file {pr_description_file} | ||
| ``` | ||
| - If `gh` is not available: | ||
| - Save the PR description to `FEATURE_DIR/releases/pr-description-{timestamp}.md` | ||
| - Provide instructions for manual PR creation | ||
| - Output the PR title and description for copy-paste |
There was a problem hiding this comment.
PR creation is called out as requiring explicit user confirmation in the operating constraints, but Step 8 goes straight to gh pr create without an explicit confirmation prompt right before executing it. Add a confirm gate (default "no") prior to PR creation, and ensure the manual fallback also asks before writing the PR description file if you're treating file writes as destructive.
| ```bash | |
| gh pr create --base {target_branch} --head {feature_branch} --title "{PR title}" --body-file {pr_description_file} | |
| ``` | |
| - If `gh` is not available: | |
| - Save the PR description to `FEATURE_DIR/releases/pr-description-{timestamp}.md` | |
| - Provide instructions for manual PR creation | |
| - Output the PR title and description for copy-paste | |
| - Prompt the user for explicit confirmation **right before** creating the PR (default **no**). For example: | |
| ```bash | |
| read -r -p "Proceed to create a GitHub pull request now? [y/N] " confirm_pr | |
| case "${confirm_pr}" in | |
| [Yy][Ee][Ss]|[Yy]) | |
| gh pr create --base {target_branch} --head {feature_branch} --title "{PR title}" --body-file {pr_description_file} | |
| ;; | |
| *) | |
| echo "Skipping PR creation; you can run 'gh pr create' manually later." | |
| ;; | |
| esac | |
| ``` | |
| - If `gh` is not available: | |
| - Prompt the user for explicit confirmation **before** writing any PR description file (default **no**). For example: | |
| ```bash | |
| read -r -p "Save PR description and proceed with manual PR creation steps? [y/N] " confirm_pr_file | |
| case "${confirm_pr_file}" in | |
| [Yy][Ee][Ss]|[Yy]) | |
| pr_description_path="FEATURE_DIR/releases/pr-description-{timestamp}.md" | |
| # Write PR description content to "${pr_description_path}" | |
| echo "PR description saved to ${pr_description_path}." | |
| echo "Use this file when creating your PR manually in the Git provider UI." | |
| ;; | |
| *) | |
| echo "Skipping PR description file write and manual PR instructions." | |
| ;; | |
| esac | |
| ``` | |
| - Provide instructions for manual PR creation | |
| - Output the PR title and description for copy-paste (only if the user confirmed saving or otherwise requested output) |
Add a new core command that automates the full release pipeline: 1. Pre-flight readiness checks (tasks, review, QA, working tree) 2. Branch synchronization with target branch 3. Changelog generation from spec + tasks + git commits 4. CI pipeline verification 5. PR creation with full traceability to .specify/ artifacts 6. Release artifact archival Supports GitHub CLI (gh) for automated PR creation with fallback to manual flow. Inspired by GStack's /ship command.
04875ca to
b17b14e
Compare
|
Please deliver this as an extension. See https://github.com/github/spec-kit/tree/main/extensions |
- Use {remote_name} consistently instead of hard-coded 'origin'
- Add explicit user confirmation before rebase/merge operations
- Add explicit user confirmation before push operations
- Add explicit user confirmation before PR creation
|
Thanks for the review! All 4 comments addressed in the latest push: 1-2. Hard-coded origin: Replaced all hard-coded |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
templates/commands/ship.md
Outdated
|
|
||
| 3. **Determine Shipping Configuration**: | ||
| - Detect the current feature branch: `git branch --show-current` | ||
| - Determine the target branch (default: `main`; override via user input or `.specify/config.yml`) |
There was a problem hiding this comment.
Determine the target branch mentions overriding via .specify/config.yml, but this repo doesn’t reference a project-level .specify/config.yml anywhere else (search only finds this line). This is likely misleading; either document the actual supported config location/key (if one exists) or remove this reference and rely on user input/defaults.
| - Determine the target branch (default: `main`; override via user input or `.specify/config.yml`) | |
| - Determine the target branch (default: `main`; allow override via user input/prompt) |
templates/commands/ship.md
Outdated
| - If any tasks are incomplete: **STOP** and warn. Ask user to confirm proceeding or run `/speckit.implement` first. | ||
|
|
||
| **Review Status** (if FEATURE_DIR/reviews/ exists): | ||
| - Read the most recent review report | ||
| - If verdict is ❌ CHANGES REQUIRED: **STOP** and warn. Recommend running `/speckit.review` after fixes. | ||
| - If verdict is ⚠️ APPROVED WITH CONDITIONS: Warn but allow proceeding with confirmation. | ||
|
|
||
| **QA Status** (if FEATURE_DIR/qa/ exists): | ||
| - Read the most recent QA report | ||
| - If verdict is ❌ QA FAILED: **STOP** and warn. Recommend running `/speckit.qa` after fixes. | ||
| - If verdict is ⚠️ QA PASSED WITH NOTES: Warn but allow proceeding with confirmation. |
There was a problem hiding this comment.
This flow recommends /speckit.review and /speckit.qa, but those commands don’t appear to exist in this repository (no corresponding templates/commands entries, and no other references found). Either add/land those commands first, gate these instructions behind “if available”, or update the guidance to match the currently shipped command set.
templates/commands/ship.md
Outdated
| ```bash | ||
| gh pr create --base {target_branch} --head {feature_branch} --title "{PR title}" --body-file {pr_description_file} | ||
| ``` | ||
| - If `gh` is not available: | ||
| - Prompt the user for explicit confirmation **before** writing the PR description file (default **no**) | ||
| - Save the PR description to `FEATURE_DIR/releases/pr-description-{timestamp}.md` |
There was a problem hiding this comment.
The gh pr create example uses --body-file {pr_description_file}, but the template never defines/writes {pr_description_file} in the gh-available path (it only specifies a filename when gh is unavailable). Clarify where the PR body file is written (ideally always write it to FEATURE_DIR/releases/pr-description-{timestamp}.md and reuse that path for both branches).
| ```bash | |
| gh pr create --base {target_branch} --head {feature_branch} --title "{PR title}" --body-file {pr_description_file} | |
| ``` | |
| - If `gh` is not available: | |
| - Prompt the user for explicit confirmation **before** writing the PR description file (default **no**) | |
| - Save the PR description to `FEATURE_DIR/releases/pr-description-{timestamp}.md` | |
| - Write the PR description to `FEATURE_DIR/releases/pr-description-{timestamp}.md` and use that path as `{pr_description_file}`: | |
| ```bash | |
| gh pr create --base {target_branch} --head {feature_branch} --title "{PR title}" --body-file {pr_description_file} | |
| ``` | |
| - If `gh` is not available: | |
| - Prompt the user for explicit confirmation **before** writing the PR description file (default **no**) | |
| - Save the PR description to the same path: `FEATURE_DIR/releases/pr-description-{timestamp}.md` |
|
|
||
| Next steps: | ||
| - Review the PR at {pr_url} | ||
| - After merge, run `/speckit.retro` for a retrospective | ||
| ``` |
There was a problem hiding this comment.
The post-ship message suggests running /speckit.retro, but that command doesn’t exist in this repository (no template/command file, and no other references found). Either add the retro command in the same PR series or adjust this “Next steps” guidance to only mention commands that are currently available.
|
Please review https://github.com/github/spec-kit/blob/main/extensions/EXTENSION-PUBLISHING-GUIDE.md for publishing a community hosted extension |
- Remove misleading .specify/config.yml reference for target branch - Gate /speckit.review, /speckit.qa, /speckit.retro references with (if available) - Always write PR description to FEATURE_DIR/releases/ before gh pr create - Align QA verdict terminology with qa command (PARTIAL PASS/FAILURES FOUND)
|
Addressed the 4 new review comments in the latest push:
Regarding the extension delivery model — will respond separately. |
|
Thanks @mnriem! I've reviewed the Extension Publishing Guide. Will restructure this as a community extension following the extension.yml manifest pattern and resubmit as an extension contribution to catalog.community.json. |
|
Closing in favor of PR #2049 — restructured as a community extension per maintainer feedback. Extension repo and v1.0.0 release are available. |
Description
Problem
Spec-Kit generates GitHub issues from tasks (
/speckit.taskstoissues), but has no command to automate the actual release process. After implementation, review, and QA, developers must manually: verify all tasks are complete, sync branches, write changelog entries, check CI status, compose PR descriptions, and create the pull request. This manual process is error-prone, inconsistent, and loses the traceability that spec-driven development creates.The
.specify/directory contains rich artifacts (spec, plan, tasks, review reports, QA reports) that should flow directly into a well-structured PR — but today this connection is broken.Industry context: GStack's
/shipcommand demonstrates that automating the release pipeline — with pre-flight checks, CI verification, and artifact-backed PR descriptions — eliminates an entire class of release errors and saves significant time per feature.Solution
Adds
/speckit.ship— a new core command that automates the complete release engineering workflow:gh), waits for completion, reports failures.specify/artifacts with full traceabilitySafe by default: Every destructive operation (force push, PR creation) requires explicit user confirmation.
Graceful fallback: If
ghCLI is unavailable, generates PR description as a markdown file for manual creation.Business Value
Workflow Position
Files Added
templates/commands/ship.mdbefore_ship,after_ship)templates/ship-template.mdTesting
uv run specify --helpuv sync && uv run pytest— 878/972 pass (44 failures are pre-existing, unrelated to this change)specify initAI Disclosure
implement.mdandanalyze.md. All content was reviewed and validated against the project's template conventions.