Skill Tracer
Skills › gstack › land-and-deploy › first-run-validation.md

first-run-validation.md

188 lines · land-and-deploy/sections/first-run-validation.md at 3f79d81

Open on GitHub

Most marks come from matching words in the file, so treat them as pointers.

1<!-- AUTO-GENERATED from first-run-validation.md.tmpl — do not edit directly -->
2<!-- Regenerate: bun run gen:skill-docs -->

<!-- AUTO-GENERATED from first-run-validation.md.tmpl — do not edit directly --> <!-- Regenerate: bun run gen:skill-docs -->

Raw lines 1 to 2
3## Step 1.5 (dry-run flow): First-run / config-changed validation
4
5You are here because the Step 1.5 detection in the skeleton printed `FIRST_RUN`
6or `CONFIG_CHANGED` (a `CONFIRMED` run never reads this section). Nothing has
7been merged or deployed yet.
8
9**CONFIG_CHANGED:** Say "Your deploy configuration changed; I'll validate it again."
10**FIRST_RUN:** Say "This is the first run for this project. I'll detect the setup,
11test read-only access, and show you what merge would trigger before asking for approval."
12Both follow 1.5a-e. Explain each check in plain English; nothing deploys in this dry run.

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.

CONFIG_CHANGED: Say "Your deploy configuration changed; I'll validate it again." FIRST_RUN: Say "This is the first run for this project. I'll detect the setup, test read-only access, and show you what merge would trigger before asking for approval." Both follow 1.5a-e. Explain each check in plain English; nothing deploys in this dry run.

Raw lines 3 to 13
14### 1.5a: Deploy infrastructure detection
16Run the deploy configuration bootstrap to detect the platform and settings:
18```bash
19# Check for persisted deploy config in CLAUDE.md
20DEPLOY_CONFIG=$(grep -A 20 "## Deploy Configuration" CLAUDE.md 2>/dev/null || echo "NO_CONFIG")
21echo "$DEPLOY_CONFIG"
23# If config exists, parse it
24if [ "$DEPLOY_CONFIG" != "NO_CONFIG" ]; then
25 # Cut at the FIRST ": ", not the last. A greedy 's/.*: *//' ate the scheme of
26 # any URL: "Production URL: https://x.com" became "//x.com", because the last
27 # ":" belongs to "https:".
28 PROD_URL=$(echo "$DEPLOY_CONFIG" | grep -i "production.*url" | head -1 | sed 's/^[^:]*: *//')
29 PLATFORM=$(echo "$DEPLOY_CONFIG" | grep -i "platform" | head -1 | sed 's/^[^:]*: *//')
30 echo "PERSISTED_PLATFORM:$PLATFORM"
31 echo "PERSISTED_URL:$PROD_URL"
32fi
34# Auto-detect platform from config files
35[ -f fly.toml ] && echo "PLATFORM:fly"
36[ -f render.yaml ] && echo "PLATFORM:render"
37([ -f vercel.json ] || [ -d .vercel ]) && echo "PLATFORM:vercel"
38[ -f netlify.toml ] && echo "PLATFORM:netlify"
39[ -f Procfile ] && echo "PLATFORM:heroku"
40([ -f railway.json ] || [ -f railway.toml ]) && echo "PLATFORM:railway"
42# Detect deploy workflows
43for f in $(find .github/workflows -maxdepth 1 \( -name '*.yml' -o -name '*.yaml' \) 2>/dev/null); do
44 [ -f "$f" ] && grep -qiE "deploy|release|production|cd" "$f" 2>/dev/null && echo "DEPLOY_WORKFLOW:$f"
45 [ -f "$f" ] && grep -qiE "staging" "$f" 2>/dev/null && echo "STAGING_WORKFLOW:$f"
46done
47```
49If `PERSISTED_PLATFORM` and `PERSISTED_URL` were found in CLAUDE.md, use them directly
50and skip manual detection. If no persisted config exists, use the auto-detected platform
51to guide deploy verification. If nothing is detected, ask the user via AskUserQuestion
52in the decision tree below.
54If you want to persist deploy settings for future runs, suggest the user run `/setup-deploy`.
56Parse the output and record: the detected platform, production URL, deploy workflow (if any),
57and any persisted config from CLAUDE.md.

1.5a: Deploy infrastructure detection

Run the deploy configuration bootstrap to detect the platform and settings:

# 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.

Raw lines 14 to 58
Project instruction file CLAUDE.md :19
Involves a person ask the user :51
Involves a person AskUserQuestion :51
Names a skill setup-deploy :54
59### 1.5b: Command validation
61Test each detected read-only command. Gather staging facts in 1.5c before displaying
62the combined validation table below; no deploy trigger runs during this dry run.
64```bash
65# Test gh auth (already passed in Step 1, but confirm)
66gh auth status 2>&1 | head -3
68# Test platform CLI if detected
69# Fly.io: fly status --app {app} 2>/dev/null
70# Heroku: heroku releases --app {app} -n 1 2>/dev/null
71# Vercel: vercel ls 2>/dev/null | head -3
73# Test production URL reachability
74# curl -sf {production-url} -o /dev/null -w "%{http_code}" 2>/dev/null
75```
77Run whichever commands are relevant based on the detected platform. Build the results into this table:
79```
80╔══════════════════════════════════════════════════════════╗
81║ DEPLOY INFRASTRUCTURE VALIDATION ║
82╠══════════════════════════════════════════════════════════╣
83║ Platform: {platform} (from {source}) ║
84║ App: {app name or "N/A"} ║
85║ Prod URL: {url or "not configured"} ║
86║ COMMAND VALIDATION ║
87║ ├─ gh auth status: ✓ PASS ║
88║ ├─ {platform CLI}: ✓ PASS / ⚠ NOT INSTALLED / ✗ FAIL ║
89║ ├─ curl prod URL: ✓ PASS (200 OK) / ⚠ UNREACHABLE ║
90║ └─ deploy workflow: {file or "none detected"} ║
91║ STAGING DETECTION ║
92║ ├─ Staging URL: {url or "not configured"} ║
93║ ├─ Staging workflow: {file or "not found"} ║
94║ └─ Preview deploys: {detected or "not detected"} ║
95║ WHAT WILL HAPPEN ║
96║ 1. Wait for all CI on this head (gstack-ci-gate) ║
97║ 2. Readiness checks and explicit merge approval ║
98║ 3. Merge PR via {merge method} ║
99║ 4. {Wait for deploy workflow / Wait 60s / Skip} ║
100║ 5. {Run canary verification / Skip (no URL)} ║
101║ MERGE METHOD: {squash/merge/rebase} (from repo settings) ║
102║ MERGE QUEUE: {detected / not detected} ║
103╚══════════════════════════════════════════════════════════╝
104```
106**Validation failures are WARNINGs, not BLOCKERs** (except `gh auth status` which already
107failed at Step 1). If `curl` fails, note "I couldn't reach that URL — might be a network
108issue, VPN requirement, or incorrect address. Health verification is unavailable."
109If platform CLI is not installed, note "The {platform} CLI isn't installed on this machine.
110Platform status is unavailable. HTTP checks can show reachability, not a deployed
111revision; I'll need the configured trigger and its deployment evidence."

1.5b: Command validation

Test each detected read-only command. Gather staging facts in 1.5c before displaying the combined validation table below; no deploy trigger runs during this dry run.

# 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. Wait for all CI on this head (gstack-ci-gate)         ║
║  2. Readiness checks and explicit merge approval          ║
║  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. Health verification is unavailable." If platform CLI is not installed, note "The {platform} CLI isn't installed on this machine. Platform status is unavailable. HTTP checks can show reachability, not a deployed revision; I'll need the configured trigger and its deployment evidence."

Raw lines 59 to 112
113### 1.5c: Staging detection
115Check for staging environments in this order:
1171. **CLAUDE.md persisted config:** Check for a staging URL in the Deploy Configuration section:
118```bash
119grep -i "staging" CLAUDE.md 2>/dev/null | head -3
120```
1222. **GitHub Actions staging workflow:** Check for workflow files with "staging" in the name or content:
123```bash
124for f in $(find .github/workflows -maxdepth 1 \( -name '*.yml' -o -name '*.yaml' \) 2>/dev/null); do
125 [ -f "$f" ] && grep -qiE "staging" "$f" 2>/dev/null && echo "STAGING_WORKFLOW:$f"
126done
127```
1293. **Vercel/Netlify preview deploys:** Check PR status checks for preview URLs:
130```bash
131gh pr checks "$PR_NUMBER" --repo "$REPO" --json name,state,bucket,link
132```
133Look for check names containing "vercel", "netlify", or "preview" and inspect the
134link for a preview URL. A check-details link is not necessarily the preview itself.
136Record candidates, not proof that this revision is deployed. Step 3.5 refreshes
137deployment facts on every run, and handles any true staging-first request before merge.

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:
grep -i "staging" CLAUDE.md 2>/dev/null | head -3
  1. GitHub Actions staging workflow: Check for workflow files with "staging" in the name or content:
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
  1. Vercel/Netlify preview deploys: Check PR status checks for preview URLs:
gh pr checks "$PR_NUMBER" --repo "$REPO" --json name,state,bucket,link

Look for check names containing "vercel", "netlify", or "preview" and inspect the link for a preview URL. A check-details link is not necessarily the preview itself.

Record candidates, not proof that this revision is deployed. Step 3.5 refreshes deployment facts on every run, and handles any true staging-first request before merge.

Raw lines 113 to 138
Project instruction file CLAUDE.md :117
139### 1.5d: Readiness preview
141Tell 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."
143Preview the readiness checks that will run at Step 3.5 (without re-running tests):
145```bash
146~/.claude/skills/gstack/bin/gstack-review-read 2>/dev/null
147```
149Show a summary of review status: which reviews have been run, how stale they are.
150Also check if CHANGELOG.md and VERSION have been updated.
152Explain 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.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):

~/.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."

Raw lines 139 to 153
Runs code bin/gstack-review-read :146
154### 1.5e: Dry-run confirmation
156Tell the user: "That's everything I detected. Take a look at the table above — does this match how your project actually deploys?"
158Present the full dry-run results to the user via AskUserQuestion:
160- **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."
161- Show the infrastructure validation table from 1.5b above.
162- List any warnings from command validation, with plain-English explanations.
163- If staging was detected: "I found {url/workflow}. A staging URL does not prove production is held. Before merge I'll check the triggers; afterward I can verify an existing staging deployment, but production may already be live."
164- If no staging was detected: "I didn't find staging. I'll identify what merge triggers before asking you to approve it."
165- **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.
166- A) That's right — this is how my project deploys. Let's go. (Completeness: 10/10)
167- B) Something's off — let me tell you what's wrong (Completeness: 10/10)
168- C) I want to configure this more carefully first (runs /setup-deploy) (Completeness: 10/10)
170**If A:** Say "Setup confirmed. I'll save its fingerprint, skip unchanged dry runs,
171and still refresh deployment facts before each merge approval."
173Save the deploy config fingerprint so we can detect future changes:
174```bash
175GSTACK_STATE_ROOT=$(~/.claude/skills/gstack/bin/gstack-paths --get GSTACK_STATE_ROOT); : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}"
176SLUG=$(~/.claude/skills/gstack/bin/gstack-slug --get SLUG 2>/dev/null)
177mkdir -p "$GSTACK_STATE_ROOT"/projects/$SLUG
178CURRENT_HASH=$(sed -n '/## Deploy Configuration/,/^## /p' CLAUDE.md 2>/dev/null | shasum -a 256 | cut -d' ' -f1)
179WORKFLOW_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)
180echo "${CURRENT_HASH}-${WORKFLOW_HASH}" > "$GSTACK_STATE_ROOT"/projects/$SLUG/land-deploy-confirmed
181```
182Continue to Step 2.
184**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."
186**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."
188---

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: "I found {url/workflow}. A staging URL does not prove production is held. Before merge I'll check the triggers; afterward I can verify an existing staging deployment, but production may already be live."
  • If no staging was detected: "I didn't find staging. I'll identify what merge triggers before asking you to approve it."
  • 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: Say "Setup confirmed. I'll save its fingerprint, skip unchanged dry runs, and still refresh deployment facts before each merge approval."

Save the deploy config fingerprint so we can detect future changes:

GSTACK_STATE_ROOT=$(~/.claude/skills/gstack/bin/gstack-paths --get GSTACK_STATE_ROOT); : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}"
SLUG=$(~/.claude/skills/gstack/bin/gstack-slug --get SLUG 2>/dev/null)
mkdir -p "$GSTACK_STATE_ROOT"/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_STATE_ROOT"/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."


Raw lines 154 to 188
Involves a person AskUserQuestion :158
Runs code bin/gstack-paths :175
Runs code bin/gstack-slug :176
Project instruction file CLAUDE.md :178
Names a skill setup-deploy :184