## Step 1.5 (dry-run flow): First-run / config-changed validation You are here because the Step 1.5 detection in the skeleton printed `FIRST_RUN` or `CONFIG_CHANGED` (a `CONFIRMED` run never reads this section). Nothing has been merged or deployed yet. **If CONFIG_CHANGED:** The deploy configuration has changed since the last confirmed deploy. Re-trigger the dry run. Tell the user: "I've deployed this project before, but your deploy configuration has changed since the last time. That could mean a new platform, a different workflow, or updated URLs. I'm going to do a quick dry run to make sure I still understand how your project deploys." Then proceed to the FIRST_RUN flow below (steps 1.5a through 1.5e). **If FIRST_RUN:** This is the first time `/land-and-deploy` is running for this project. Before doing anything irreversible, show the user exactly what will happen. This is a dry run — explain, validate, and confirm. Tell the user: "This is the first time I'm deploying this project, so I'm going to do a dry run first. Here's what that means: I'll detect your deploy infrastructure, test that my commands actually work, and show you exactly what will happen — step by step — before I touch anything. Deploys are irreversible once they hit production, so I want to earn your trust before I start merging. Let me take a look at your setup." ### 1.5a: Deploy infrastructure detection Run the deploy configuration bootstrap to detect the platform and settings: ```bash # Check for persisted deploy config in CLAUDE.md DEPLOY_CONFIG=$(grep -A 20 "## Deploy Configuration" CLAUDE.md 2>/dev/null || echo "NO_CONFIG") echo "$DEPLOY_CONFIG" # If config exists, parse it if [ "$DEPLOY_CONFIG" != "NO_CONFIG" ]; then # Cut at the FIRST ": ", not the last. A greedy 's/.*: *//' ate the scheme of # any URL: "Production URL: https://x.com" became "//x.com", because the last # ":" belongs to "https:". PROD_URL=$(echo "$DEPLOY_CONFIG" | grep -i "production.*url" | head -1 | sed 's/^[^:]*: *//') PLATFORM=$(echo "$DEPLOY_CONFIG" | grep -i "platform" | head -1 | sed 's/^[^:]*: *//') echo "PERSISTED_PLATFORM:$PLATFORM" echo "PERSISTED_URL:$PROD_URL" fi # Auto-detect platform from config files [ -f fly.toml ] && echo "PLATFORM:fly" [ -f render.yaml ] && echo "PLATFORM:render" ([ -f vercel.json ] || [ -d .vercel ]) && echo "PLATFORM:vercel" [ -f netlify.toml ] && echo "PLATFORM:netlify" [ -f Procfile ] && echo "PLATFORM:heroku" ([ -f railway.json ] || [ -f railway.toml ]) && echo "PLATFORM:railway" # Detect deploy workflows for f in $(find .github/workflows -maxdepth 1 \( -name '*.yml' -o -name '*.yaml' \) 2>/dev/null); do [ -f "$f" ] && grep -qiE "deploy|release|production|cd" "$f" 2>/dev/null && echo "DEPLOY_WORKFLOW:$f" [ -f "$f" ] && grep -qiE "staging" "$f" 2>/dev/null && echo "STAGING_WORKFLOW:$f" done ``` If `PERSISTED_PLATFORM` and `PERSISTED_URL` were found in CLAUDE.md, use them directly and skip manual detection. If no persisted config exists, use the auto-detected platform to guide deploy verification. If nothing is detected, ask the user via AskUserQuestion in the decision tree below. If you want to persist deploy settings for future runs, suggest the user run `/setup-deploy`. Parse the output and record: the detected platform, production URL, deploy workflow (if any), and any persisted config from CLAUDE.md. ### 1.5b: Command validation Test each detected command to verify the detection is accurate. Build a validation table: ```bash # Test gh auth (already passed in Step 1, but confirm) gh auth status 2>&1 | head -3 # Test platform CLI if detected # Fly.io: fly status --app {app} 2>/dev/null # Heroku: heroku releases --app {app} -n 1 2>/dev/null # Vercel: vercel ls 2>/dev/null | head -3 # Test production URL reachability # curl -sf {production-url} -o /dev/null -w "%{http_code}" 2>/dev/null ``` Run whichever commands are relevant based on the detected platform. Build the results into this table: ``` ╔══════════════════════════════════════════════════════════╗ ║ DEPLOY INFRASTRUCTURE VALIDATION ║ ╠══════════════════════════════════════════════════════════╣ ║ ║ ║ Platform: {platform} (from {source}) ║ ║ App: {app name or "N/A"} ║ ║ Prod URL: {url or "not configured"} ║ ║ ║ ║ COMMAND VALIDATION ║ ║ ├─ gh auth status: ✓ PASS ║ ║ ├─ {platform CLI}: ✓ PASS / ⚠ NOT INSTALLED / ✗ FAIL ║ ║ ├─ curl prod URL: ✓ PASS (200 OK) / ⚠ UNREACHABLE ║ ║ └─ deploy workflow: {file or "none detected"} ║ ║ ║ ║ STAGING DETECTION ║ ║ ├─ Staging URL: {url or "not configured"} ║ ║ ├─ Staging workflow: {file or "not found"} ║ ║ └─ Preview deploys: {detected or "not detected"} ║ ║ ║ ║ WHAT WILL HAPPEN ║ ║ 1. Run pre-merge readiness checks (reviews, tests, docs) ║ ║ 2. Wait for CI if pending ║ ║ 3. Merge PR via {merge method} ║ ║ 4. {Wait for deploy workflow / Wait 60s / Skip} ║ ║ 5. {Run canary verification / Skip (no URL)} ║ ║ ║ ║ MERGE METHOD: {squash/merge/rebase} (from repo settings) ║ ║ MERGE QUEUE: {detected / not detected} ║ ╚══════════════════════════════════════════════════════════╝ ``` **Validation failures are WARNINGs, not BLOCKERs** (except `gh auth status` which already failed at Step 1). If `curl` fails, note "I couldn't reach that URL — might be a network issue, VPN requirement, or incorrect address. I'll still be able to deploy, but I won't be able to verify the site is healthy afterward." If platform CLI is not installed, note "The {platform} CLI isn't installed on this machine. I can still deploy through GitHub, but I'll use HTTP health checks instead of the platform CLI to verify the deploy worked." ### 1.5c: Staging detection Check for staging environments in this order: 1. **CLAUDE.md persisted config:** Check for a staging URL in the Deploy Configuration section: ```bash grep -i "staging" CLAUDE.md 2>/dev/null | head -3 ``` 2. **GitHub Actions staging workflow:** Check for workflow files with "staging" in the name or content: ```bash for f in $(find .github/workflows -maxdepth 1 \( -name '*.yml' -o -name '*.yaml' \) 2>/dev/null); do [ -f "$f" ] && grep -qiE "staging" "$f" 2>/dev/null && echo "STAGING_WORKFLOW:$f" done ``` 3. **Vercel/Netlify preview deploys:** Check PR status checks for preview URLs: ```bash gh pr checks --json name,targetUrl 2>/dev/null | head -20 ``` Look for check names containing "vercel", "netlify", or "preview" and extract the target URL. Record any staging targets found. These will be offered in Step 5. ### 1.5d: Readiness preview Tell the user: "Before I merge any PR, I run a series of readiness checks — code reviews, tests, documentation, PR accuracy. Let me show you what that looks like for this project." Preview the readiness checks that will run at Step 3.5 (without re-running tests): ```bash ~/.claude/skills/gstack/bin/gstack-review-read 2>/dev/null ``` Show a summary of review status: which reviews have been run, how stale they are. Also check if CHANGELOG.md and VERSION have been updated. Explain in plain English: "When I merge, I'll check: has the code been reviewed recently? Do the tests pass? Is the CHANGELOG updated? Is the PR description accurate? If anything looks off, I'll flag it before merging." ### 1.5e: Dry-run confirmation Tell the user: "That's everything I detected. Take a look at the table above — does this match how your project actually deploys?" Present the full dry-run results to the user via AskUserQuestion: - **Re-ground:** "First deploy dry-run for [project] on branch [branch]. Above is what I detected about your deploy infrastructure. Nothing has been merged or deployed yet — this is just my understanding of your setup." - Show the infrastructure validation table from 1.5b above. - List any warnings from command validation, with plain-English explanations. - If staging was detected, note: "I found a staging environment at {url/workflow}. After we merge, I'll offer to deploy there first so you can verify everything works before it hits production." - If no staging was detected, note: "I didn't find a staging environment. The deploy will go straight to production — I'll run health checks right after to make sure everything looks good." - **RECOMMENDATION:** Choose A if all validations passed. Choose B if there are issues to fix. Choose C to run /setup-deploy for a more thorough configuration. - A) That's right — this is how my project deploys. Let's go. (Completeness: 10/10) - B) Something's off — let me tell you what's wrong (Completeness: 10/10) - C) I want to configure this more carefully first (runs /setup-deploy) (Completeness: 10/10) **If A:** Tell the user: "Great — I've saved this configuration. Next time you run `/land-and-deploy`, I'll skip the dry run and go straight to readiness checks. If your deploy setup changes (new platform, different workflows, updated URLs), I'll automatically re-run the dry run to make sure I still have it right." Save the deploy config fingerprint so we can detect future changes: ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" mkdir -p ~/.gstack/projects/$SLUG CURRENT_HASH=$(sed -n '/## Deploy Configuration/,/^## /p' CLAUDE.md 2>/dev/null | shasum -a 256 | cut -d' ' -f1) WORKFLOW_HASH=$(find .github/workflows -maxdepth 1 \( -name '*deploy*' -o -name '*cd*' \) 2>/dev/null | xargs cat 2>/dev/null | shasum -a 256 | cut -d' ' -f1) echo "${CURRENT_HASH}-${WORKFLOW_HASH}" > ~/.gstack/projects/$SLUG/land-deploy-confirmed ``` Continue to Step 2. **If B:** **STOP.** "Tell me what's different about your setup and I'll adjust. You can also run `/setup-deploy` to walk through the full configuration." **If C:** **STOP.** "Running `/setup-deploy` will walk through your deploy platform, production URL, and health checks in detail. It saves everything to CLAUDE.md so I'll know exactly what to do next time. Run `/land-and-deploy` again when that's done." ---