| name | staging-deployment-phase |
| description | Deploys features to staging environment with database migrations, health checks, and deployment verification. Use during /ship-staging phase or when deploying to staging environments for validation before production release. |
- Verify PR merged: Confirm pull request merged to main branch
- Run migrations: Execute database migrations if schema changes exist
- Trigger deployment: Deploy to staging environment via CI/CD or manual trigger
- Monitor deployment: Watch deployment logs and progress
- Run health checks: Verify all services and endpoints operational
- Generate report: Create staging-ship-report.md with deployment metadata
Example workflow:
Verifying PR merged... ✅
Merge commit: abc123f "Merge pull request #47"
Checking for migrations...
Found: migrations/2025_11_add_user_profile.sql
Running migrations... ✅
Triggering staging deployment...
Platform: Vercel/Netlify/Railway
Deployment ID: dpl_abc123
Status: Building... → Deploying... → Live ✅
Running health checks...
API: https://staging.example.com/health → 200 OK ✅
Database: Connected ✅
Cache: Operational ✅
Staging deployment successful!
Ship report: specs/NNN-slug/staging-ship-report.md
Staging URL: https://staging.example.com
/ship-stagingcommand executed- User mentions "deploy to staging", "ship to staging", "staging deployment"
- state.yaml shows current_phase: ship-staging
- Deployment model is staging-prod (not direct-prod or local-only)
Prerequisites:
- PR successfully merged to main branch
- CI/CD passing on main branch
- Staging environment configured
- Database migrations prepared (if needed)
Confirm pull request successfully merged before deploying.
Validation:
# Check recent commits for merge commit
git log --oneline -10 | grep "Merge pull request"
# Verify feature branch merged
FEATURE_SLUG="user-profile-editing"
git log --oneline --all | grep -i "$FEATURE_SLUG"
# Confirm on main branch
CURRENT_BRANCH=$(git branch --show-current)
if [ "$CURRENT_BRANCH" != "main" ]; then
echo "❌ Not on main branch (currently on $CURRENT_BRANCH)"
echo "Switch to main: git checkout main && git pull"
exit 1
fi
echo "✅ PR merged to main"
Blocking conditions:
- Not on main branch → Switch to main first
- No recent merge commit → Feature branch not merged yet
- CI failing on main → Fix CI before deploying
Output:
✅ PR merged to main
Merge commit: abc123f "Merge pull request #47: Add user profile editing"
Author: @username
Merged: 2 minutes ago
Execute database migrations if schema changes exist.
Detection:
# Check for migration files (Node.js example)
if [ -d "migrations" ] && [ -n "$(ls -A migrations/*.sql 2>/dev/null)" ]; then
echo "✅ Migrations detected"
# List migrations
ls -1 migrations/*.sql
# Run migrations
npm run migrate:staging
# Or with migration tool
# knex migrate:latest --env staging
# prisma migrate deploy --schema=./schema.prisma
# Verify migrations applied
echo "✅ Migrations completed"
else
echo "ℹ️ No migrations to run"
fi
Error handling:
# If migration fails
if [ $? -ne 0 ]; then
echo "❌ Migration failed"
echo "Review migration logs for errors"
echo "DO NOT proceed with deployment"
echo ""
echo "Common issues:"
echo " - Syntax errors in SQL"
echo " - Constraint violations"
echo " - Missing columns in dependent tables"
exit 1
fi
Rollback procedure:
# If deployment fails after migration
# Rollback migrations
npm run migrate:rollback:staging
# Or manually revert last migration
# psql -h staging-db.example.com -d mydb -f migrations/rollback/2025_11_revert.sql
Deploy to staging environment based on deployment platform.
Platform-specific commands:
Vercel:
# Deploy to staging (preview environment)
vercel --yes
# Or trigger via git push
git push origin main
# Vercel auto-deploys main → staging
Netlify:
# Deploy to staging environment
netlify deploy --prod=false
# Or via git push to staging branch
git push origin main:staging
Railway:
# Trigger deployment
railway up --environment staging
GitHub Actions:
# Trigger workflow
gh workflow run deploy-staging.yml
# Monitor workflow
gh run watch
Docker/Custom:
# Build and deploy
docker build -t myapp:staging .
docker push registry.example.com/myapp:staging
# SSH to staging server
ssh staging.example.com
docker pull registry.example.com/myapp:staging
docker-compose up -d
Monitor deployment:
# Get deployment ID/URL
DEPLOYMENT_ID="dpl_abc123xyz"
STAGING_URL="https://staging.example.com"
echo "Deployment ID: $DEPLOYMENT_ID"
echo "Staging URL: $STAGING_URL"
echo ""
echo "Monitoring deployment..."
# Platform-specific monitoring commands
Verify all services and endpoints operational after deployment.
Critical checks:
echo "Running health checks..."
# Check 1: API health endpoint
API_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$STAGING_URL/health")
if [ "$API_STATUS" = "200" ]; then
echo " ✅ API: $API_STATUS OK"
else
echo " ❌ API: $API_STATUS FAILED"
HEALTH_FAILED=true
fi
# Check 2: Database connectivity
DB_STATUS=$(curl -s "$STAGING_URL/health" | jq -r '.database')
if [ "$DB_STATUS" = "connected" ]; then
echo " ✅ Database: Connected"
else
echo " ❌ Database: $DB_STATUS"
HEALTH_FAILED=true
fi
# Check 3: Cache/Redis
CACHE_STATUS=$(curl -s "$STAGING_URL/health" | jq -r '.cache')
if [ "$CACHE_STATUS" = "operational" ]; then
echo " ✅ Cache: Operational"
else
echo " ❌ Cache: $CACHE_STATUS"
HEALTH_FAILED=true
fi
# Check 4: Version matches
DEPLOYED_VERSION=$(curl -s "$STAGING_URL/api/version" | jq -r '.version')
EXPECTED_VERSION=$(node -p "require('./package.json').version")
if [ "$DEPLOYED_VERSION" = "$EXPECTED_VERSION" ]; then
echo " ✅ Version: $DEPLOYED_VERSION"
else
echo " ⚠️ Version mismatch: deployed=$DEPLOYED_VERSION, expected=$EXPECTED_VERSION"
fi
# Fail if any health check failed
if [ "$HEALTH_FAILED" = "true" ]; then
echo ""
echo "❌ Health checks failed - deployment may be broken"
echo "Review logs and consider rollback"
exit 1
fi
echo ""
echo "✅ All health checks passed"
Health check endpoints:
/health- Overall system health/api/version- Deployed version/api/status- Detailed service status/api/db/ping- Database connectivity
Create staging-ship-report.md with deployment metadata.
Report generation:
FEATURE_SLUG="user-profile-editing"
REPORT_PATH="specs/001-$FEATURE_SLUG/staging-ship-report.md"
cat > "$REPORT_PATH" <<EOF
# Staging Ship Report: $FEATURE_SLUG
## Deployment Summary
- **Deployed at**: $(date -Iseconds)
- **Deployment ID**: $DEPLOYMENT_ID
- **Staging URL**: $STAGING_URL
- **Branch**: main
- **Commit**: $(git rev-parse HEAD)
- **Author**: $(git log -1 --format='%an <%ae>')
## Health Checks
- API endpoints: ✅ All passing
- Database: ✅ Connected
- Cache: ✅ Operational
- Version: ✅ $DEPLOYED_VERSION
## Database Migrations
$(if [ -d "migrations" ]; then
echo "- Migrations run: $(ls migrations/*.sql | wc -l) files"
ls -1 migrations/*.sql | sed 's/^/ - /'
else
echo "- No migrations required"
fi)
## Next Steps
- Manual validation: Test feature on staging environment
- Run: /validate-staging to approve for production
- Staging URL: $STAGING_URL
## Rollback Procedure
1. Revert deployment: [platform-specific command]
2. Rollback migrations (if any): npm run migrate:rollback:staging
3. Verify rollback successful
---
Generated by /ship-staging on $(date)
EOF
echo "✅ Ship report generated: $REPORT_PATH"
Update workflow state:
# Update state.yaml
STATE_FILE="specs/001-$FEATURE_SLUG/state.yaml"
# Add deployment metadata
yq eval ".deployment.staging.url = \"$STAGING_URL\"" -i "$STATE_FILE"
yq eval ".deployment.staging.deployment_id = \"$DEPLOYMENT_ID\"" -i "$STATE_FILE"
yq eval ".deployment.staging.deployed_at = \"$(date -Iseconds)\"" -i "$STATE_FILE"
yq eval ".current_phase = \"validate-staging\"" -i "$STATE_FILE"
echo "✅ Workflow state updated"
Show deployment results and next steps.
Summary format:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🚀 Staging Deployment Successful!
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Staging URL: https://staging.example.com
Deployment ID: dpl_abc123xyz
Deployed at: 2025-11-19T22:45:00Z
Health Checks: ✅ All passing
- API: 200 OK
- Database: Connected
- Cache: Operational
- Version: 2.7.0
Ship Report: specs/001-user-profile-editing/staging-ship-report.md
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Next: Manual Validation
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. Test feature on staging: https://staging.example.com
2. Verify all acceptance criteria met
3. Run: /validate-staging to approve for production
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Check health endpoint
curl -f https://staging.example.com/health || { echo "❌ Health check failed - deployment broken" exit 1 }
**Impact**: Broken staging deployment goes unnoticed, blocks validation
**Prevention**: Always run automated health checks, verify 200 status and service health
</pitfall>
<pitfall name="deploy_on_failing_ci">
**❌ Deploying when CI is failing on main**
```bash
# BAD: CI shows red, deploy anyway
# "Tests failing but it's probably fine..."
/ship-staging
✅ Always wait for CI to pass before deploying
# GOOD: Check CI status first
gh run list --branch main --limit 1 --json conclusion --jq '.[0].conclusion'
# If "success", proceed with deployment
# If "failure", fix CI first
Impact: Deploying broken code to staging, wasting time debugging obvious failures Prevention: Make CI passing a prerequisite gate for staging deployment
Test rollback works during /ship-staging
Before /ship-prod
**Impact**: Unable to recover quickly from broken deployment, extended downtime
**Prevention**: Document and test rollback procedure, include in ship report
</pitfall>
<pitfall name="missing_deployment_metadata">
**❌ Not recording deployment details**
```bash
# BAD: Deploy but don't save deployment ID, URL, timestamp
vercel --yes
# Later: "What version is on staging? When was it deployed?"
✅ Always generate ship report with metadata
# GOOD: Capture and save deployment info
DEPLOYMENT_ID=$(vercel --yes | grep -oP 'https://[^ ]+')
echo "Deployment ID: $DEPLOYMENT_ID" >> staging-ship-report.md
echo "Deployed at: $(date -Iseconds)" >> staging-ship-report.md
Impact: No audit trail, can't correlate staging issues with deployments Prevention: Always generate staging-ship-report.md with full metadata
- ✓ PR successfully merged to main branch (verified via git log)
- ✓ CI/CD passing on main branch
- ✓ Database migrations completed without errors (if applicable)
- ✓ Staging deployment triggered and completed successfully
- ✓ Deployment ID/URL captured
- ✓ All health check endpoints return 200 status
- ✓ API, database, cache services confirmed operational
- ✓ Deployed version matches expected version
- ✓ staging-ship-report.md generated in specs/NNN-slug/ directory
- ✓ state.yaml updated with staging deployment metadata
- ✓ Next action displayed (/validate-staging)
Quality gates passed:
- CI green on main branch before deployment
- No migration errors during schema updates
- All health checks passing post-deployment
- No critical errors in deployment logs
- Rollback procedure documented and tested