verify-deploy

Post-merge deploy verification. Waits for deploy, benchmarks production, monitors for regressions. Use after merging a PR to confirm the deploy is healthy.

9 stars

Best use case

verify-deploy is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Post-merge deploy verification. Waits for deploy, benchmarks production, monitors for regressions. Use after merging a PR to confirm the deploy is healthy.

Teams using verify-deploy should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/verify-deploy/SKILL.md --create-dirs "https://raw.githubusercontent.com/exiao/skills/main/coding/verify-deploy/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/verify-deploy/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How verify-deploy Compares

Feature / Agentverify-deployStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Post-merge deploy verification. Waits for deploy, benchmarks production, monitors for regressions. Use after merging a PR to confirm the deploy is healthy.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Verify Deploy

Post-merge deploy verification. Waits for deploy, benchmarks production, monitors for regressions. One skill to go from "just merged" to "verified in production."

Use when: a PR was just merged and you need to confirm the deploy is healthy. Never merges PRs. Assumes the user already merged.

**Requires:** Browser (for benchmarking and canary monitoring).

---

## Phase 1: LAND (Wait for Deploy)

### Detect the deploy target

Check the repo for deploy configuration:
- `render.yaml` → Render (poll the deploy API or service URL)
- `railway.json` or Railway project → Railway
- `fly.toml` → Fly.io
- `vercel.json` or `.vercel` → Vercel
- GitHub Actions deploy workflow → wait for workflow run to complete

If none found, ask the user for the production URL.

### Wait for CI

```bash
# Watch the latest commit's CI status
gh run list --branch main --limit 3
gh run watch <run_id>
```

If CI fails, stop immediately. Report the failure and the failing step. Send to Signal if a channel is available.

### Wait for deploy

Poll the production URL until it responds with 200. Timeout after 10 minutes.

```bash
# Simple health check loop
curl -s -o /dev/null -w "%{http_code}" <production_url>/health
```

If the platform has an API (Render, Railway), use it to check deploy status. Otherwise, poll the health endpoint every 15 seconds.

### Confirm deploy

Once healthy:
- Hit the health/root endpoint, confirm 200
- Check response headers for version/commit hash if available
- Report: "Deploy is live. Starting benchmark."

---

## Phase 2: BENCHMARK (Measure Production)

Browse production and measure performance on the pages affected by the PR.

### Identify affected pages

Read the merge commit or recent diff to determine which pages/routes changed:

```bash
gh pr view <latest_merged_pr> --json files --jq '.files[].path'
```

Map changed files to production routes. If unclear, benchmark the homepage + any obvious routes.

### For each page, measure:

Using the browser:

1. **Navigate** to the page
2. **Screenshot** for visual record
3. **Console errors** — capture any JS errors or warnings
4. **Page load time** — use `performance.timing` or `Performance API`:
   ```javascript
   const timing = performance.getEntriesByType('navigation')[0];
   return {
     domContentLoaded: timing.domContentLoadedEventEnd - timing.startTime,
     load: timing.loadEventEnd - timing.startTime,
     firstByte: timing.responseStart - timing.startTime
   };
   ```
5. **Core Web Vitals** (if available via `web-vitals` or PerformanceObserver):
   - LCP (Largest Contentful Paint)
   - CLS (Cumulative Layout Shift)
   - FID / INP (Interaction to Next Paint)
6. **Resource summary**:
   ```javascript
   const resources = performance.getEntriesByType('resource');
   return {
     count: resources.length,
     totalSize: resources.reduce((sum, r) => sum + (r.transferSize || 0), 0),
     slowest: resources.sort((a, b) => b.duration - a.duration).slice(0, 3).map(r => ({ name: r.name, duration: r.duration }))
   };
   ```
7. **Failed network requests** — check for 4xx/5xx in resource entries

### Report benchmark results

```
Benchmark: https://app.example.com
  Pages tested: 4
  
  /dashboard
    Load: 1.2s · TTFB: 180ms · Resources: 34 (1.8MB)
    LCP: 1.1s · CLS: 0.02
    Console errors: none
    
  /settings
    Load: 0.8s · TTFB: 150ms · Resources: 22 (950KB)
    Console errors: none
    
  Flags:
    ⚠️ /dashboard LCP > 1s
    ✅ No console errors
    ✅ No failed requests
```

If anything looks bad (load > 3s, console errors, failed requests), flag it clearly but continue to canary phase.

---

## Phase 3: CANARY (Monitor for 5 Minutes)

Post-deploy monitoring loop. Default: 5 minutes, checking every 60 seconds (5 loops).

The user can request a longer window. Respect it.

### Each loop:

1. **Hit key pages** — the same pages from benchmark phase
2. **Check console** — new JS errors since last check?
3. **Check network** — failed API calls? 5xx responses?
4. **Screenshot** — visual regression check (compare to benchmark screenshot)
5. **Health endpoint** — still 200?

### Alert criteria (send to Signal immediately, don't wait for loop):

- New console errors that weren't present in benchmark phase
- Any 5xx response from the app or its APIs
- Health endpoint returns non-200
- Page fails to load entirely
- Significant visual change from benchmark screenshot (use judgment)

### Alert format

Send to Signal with:
```
🚨 Deploy issue detected

URL: https://app.example.com/dashboard
Issue: 3 new console errors after deploy
Time: 2 minutes post-deploy

Errors:
  - TypeError: Cannot read property 'data' of undefined (main.js:1234)
  - Failed to fetch: /api/v1/portfolio (503)
  - Unhandled promise rejection (analytics.js:56)

Action needed: check the latest merge commit.
```

### Clean exit

If all loops pass with no issues:

```
✅ Deploy verified

Monitored for 5 minutes (5 checks)
Pages: 4 · Errors: 0 · Avg load: 1.1s
No regressions detected. Deploy is healthy.
```

---

## Error Handling

- **CI fails** → Stop. Report. Alert via Signal.
- **Deploy times out (>10 min)** → Stop. Report. Alert via Signal.
- **Browser can't start** → Stop. This skill requires browser.
- **Canary finds issues** → Alert immediately. Continue monitoring to see if it's transient or persistent. Report final status at end of window.

## Anti-Patterns

- Never merge PRs. The user merges.
- Don't store baselines or historical data. Each run is self-contained.
- Don't skip the canary phase even if benchmark looks clean. Bugs surface under real traffic.
- Don't silently swallow console errors. Report everything, let the user decide what matters.
- Don't run longer than requested. If the user says 5 minutes, stop at 5 minutes.

---

## Scheduled Mode (`--scheduled`)

When invoked by the daily cron job, skip Phases 1-3 and run a lighter health-check flow:

1. Curl every endpoint in the **Apps monitored** table below, record HTTP status + latency.
2. Deploy detection: `gh api` to count commits in the last 24h for each repo.
3. If recent commits exist, browser-benchmark that app's frontend (load time, TTFB, console errors, failed requests). Skip CWV/resource breakdowns.
4. No canary. Sentry handles that.

Output the "🏥 App Health Check" report format (table of endpoints + flags + optional "🆕 Recent deploys" section).

### Apps monitored

| App | Frontend URL | API URL | Endpoints | Repo |
|-----|-------------|---------|-----------|------|
| **Bloom** | `bloom.onrender.com` | `bloom.onrender.com` | `/api/health`, `/`, `/portfolios`, `/chat` | `Bloom-Invest/bloom` |
| **Bible Genius** | `app.genius.bible` | `prompt-pm--bible-fastapi-app.modal.run` | API: `/`, `/api/get_chapter?book=John&chapter=1`, `/api/search_bible?query=love` · Frontend: `/` | `prompt-pm/bible-app` |
| **InvestingArena** | `investingarena.ai` | `ai-portfolio-arena.onrender.com` | API: `/health`, `/api/portfolios`, `/api/activity` · Frontend: `/` | `Bloom-Invest/investing-log` |

**Use these URLs exactly. Do not substitute or discover alternatives.**

Related Skills

writer

9
from exiao/skills

Write content in Eric's voice — articles, blog posts, tweets, social media posts, marketing copy, newsletter drafts. Loads WRITING-STYLE.md and enforces kill phrases.

positioning-angles

9
from exiao/skills

Use when defining product positioning, choosing strategic angles, crafting value propositions, competitive positioning, product messaging, differentiation strategy, or go-to-market angles. Also use for 'how should I position my app', 'what angle should I use', 'painkiller vs vitamin', or 'market positioning'.

outline-generator

9
from exiao/skills

Use when generating outlines, article structures, content outlines, blog outlines, planning article sections, structuring posts, breaking down topics into sections, or organizing ideas for long-form content. Also use for 'outline this', 'structure this article', or 'plan the sections'.

last30days-open

9
from exiao/skills

Use only when the user explicitly asks for the open variant of last30days, including watchlists, briefings, and history queries. Sources: Reddit, X, YouTube, web.

last30days

9
from exiao/skills

Use when researching what happened in the last 30 days on a topic. Also triggered by 'last30'. Sources: Reddit, X, YouTube, web. Produces expert-level summary with copy-paste-ready prompts.

hooks

9
from exiao/skills

Use when generating hooks, headlines, titles, and scroll-stopping openers for content. Also use when analyzing viral posts, Reels, TikToks, YouTube Shorts, or successful social examples to extract reusable hook patterns and improve hook guidance.

evaluate-content

9
from exiao/skills

Use when judging content quality OR editing/improving existing copy: shareability, readability, voice, cuttability, angle, copy sweeps.

editor-in-chief

9
from exiao/skills

Use when a first draft is complete and all Phase 1 gates are done: topic selected (seo-research), title approved (hooks), outline approved (outline-generator), draft written (writer). Runs autonomous diagnosis-prescribe-rewrite loop before Substack.

copywriting

9
from exiao/skills

Write or improve marketing copy for any surface: pages, ads, app stores, landing pages, TikTok/Meta scripts, push notifications, UGC. Combines page copy frameworks with direct response principles.

content-strategy

9
from exiao/skills

Use when building content strategy: hooks, angles, and ideas from what's trending now. Covers organic and paid creative across TikTok, X, YouTube, Meta, LinkedIn.

content-pipeline

9
from exiao/skills

Orchestrator for the 3-article content pipeline — runs research phase, spawns parallel article sub-agents, creates Typefully drafts. Use when running the full content pipeline (usually via cron at 3am).

yt-dlp

9
from exiao/skills

Download audio/video from YouTube and other sites using yt-dlp. Use when the user asks to download music, songs, albums, podcasts, or video from YouTube or similar platforms. Triggers on 'download song', 'get mp3', 'yt-dlp', 'youtube download', 'rip audio'.