Agent skill
deployment-validator
Validates application readiness for Render/production deployment. Auto-runs when user mentions deployment. Prevents "works locally, fails in production" issues. Always run this BEFORE any deployment.
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/devops/deployment-validator-euda1mon1a-autonomous-assignmen
SKILL.md
Deployment Validator Skill
Purpose: Systematically validate the application is ready for production deployment, catching common "works locally, fails on Render" issues BEFORE they happen.
When to Use
This skill MUST be invoked when:
- User mentions deploying to Render, production, or staging
- User says "ready to deploy" or "push to production"
- Any git push to main/production branches
- After significant routing or static file changes
- Before creating deployment documentation
Common Issues We've Learned (Hard-Won Lessons)
Issue 1: Static File Path Mismatches
Problem: Files served from wrong directory (projectRoot vs publicDir) Symptom: Works locally, 404 in production Root Cause: Express static middleware serves from different base paths
Example:
// BAD - File in /public but served from projectRoot
app.use(express.static(projectRoot));
// Accessing /demo-scenario-picker.html serves from projectRoot/demo-scenario-picker.html
// But file is actually in projectRoot/public/demo-scenario-picker.html
// GOOD - Serve public files from publicDir
app.use(express.static(publicDir));
// Accessing /demo-scenario-picker.html serves from publicDir/demo-scenario-picker.html
Fix Pattern:
- Identify which directory the file is ACTUALLY in
- Ensure static middleware serves from that directory
- Update all references to match the served path
Issue 2: Route Precedence Overriding Static Middleware
Problem: Explicit routes defined AFTER static middleware override file serving Symptom: 404 for HTML files even though they exist Root Cause: Express matches routes in order - explicit routes win over static middleware
Example:
// BAD - Explicit route overrides static middleware
app.use(express.static(publicDir)); // Line 199
app.get('/demo-scenario-picker.html', (req, res) => { // Line 450
res.sendFile(path.join(projectRoot, 'demo-scenario-picker.html')); // WRONG PATH!
});
// GOOD - Remove explicit route, let static middleware handle it
app.use(express.static(publicDir)); // Line 199
// No explicit route needed - static middleware serves it automatically
Fix Pattern:
- Check for duplicate routes (explicit routes + static middleware)
- Remove explicit routes if static middleware already handles the file
- If explicit route is needed, ensure it uses correct path (publicDir, not projectRoot)
Issue 3: Missing Files in Production
Problem: Files exist locally but not committed/pushed Symptom: git status shows untracked files, Render can't find them Root Cause: File moved to new directory but git not updated
Example:
# File moved from projectRoot to /public but not tracked
$ git status
?? public/demo-scenario-picker.html
# File not in repo, so Render can't access it
Fix Pattern:
- Always check
git statusafter moving files git addnewly created/moved files- Verify file is in repo before deploying
Issue 4: Hardcoded Local Paths
Problem: Paths work on developer machine but not in production Symptom: ENOENT errors in production logs Root Cause: Absolute paths specific to local filesystem
Example:
// BAD - Hardcoded local path
const filePath = '/Users/developer/project/public/file.html';
// GOOD - Relative to project structure
const filePath = path.join(__dirname, '..', 'public', 'file.html');
Fix Pattern:
- Search codebase for hardcoded paths (/Users/, C:, etc.)
- Replace with path.join() using __dirname or process.cwd()
- Use environment variables for external paths
Issue 5: Environment Variable Mismatches
Problem: Different env vars locally vs production Symptom: Features work locally, fail in production Root Cause: .env file not synced with Render dashboard
Example:
# Local .env
PORT=3000
NODE_ENV=development
# Render dashboard (missing vars)
PORT=10000
# NODE_ENV not set - defaults incorrectly
Fix Pattern:
- Document all required env vars
- Verify Render dashboard has all vars set
- Use fallback values:
process.env.VAR || 'default'
Validation Checklist
Run this checklist BEFORE every deployment:
Step 1: Static File Validation
# Check which files are in /public
ls -la public/
# Verify files are tracked in git
git status
# Search for explicit routes that might override static middleware
grep -n "app.get.*\.html" src/index.ts
# Check static middleware configuration
grep -A5 "express.static" src/index.ts
Expected Results:
- All HTML files in /public are tracked by git (not in
?? untracked) - No explicit routes for files served by static middleware
- Static middleware serves from correct directory (publicDir for /public files)
Step 2: Route Precedence Check
# Find all app.get() routes in index.ts
grep -n "^app.get" src/index.ts
# Check order: static middleware should be BEFORE explicit routes
# Line numbers should be: static middleware (low) -> explicit routes (high)
Expected Results:
- Static middleware defined early (around line 199-244)
- Explicit routes defined later (after line 275)
- No duplicate routes (same path in static middleware + explicit route)
Step 3: Path Validation
# Search for hardcoded paths
grep -r "\/Users\/" src/
grep -r "C:\\\\" src/
grep -r "projectRoot" src/index.ts | grep -v "const projectRoot"
# Verify path.join usage for all file operations
grep -n "sendFile" src/index.ts
Expected Results:
- No hardcoded user-specific paths
- All sendFile() calls use path.join() with __dirname or projectRoot/publicDir
- Correct base directory (publicDir for /public files, projectRoot for root files)
Step 4: Git Status Validation
# Check for untracked files
git status --porcelain | grep "^??"
# Check for uncommitted changes
git status --porcelain | grep "^ M"
# Verify critical files are tracked
git ls-files public/ | wc -l
Expected Results:
- No untracked files in /public (unless intentionally gitignored)
- All changes committed
- All production files present in git repository
Step 5: Environment Variable Check
# List all env vars used in code
grep -r "process.env" src/ | grep -v node_modules | cut -d: -f2 | grep -o "process.env\['[^']*'\]" | sort -u
# Compare with .env.example (if exists)
cat .env.example
Expected Results:
- All required env vars documented
- Render dashboard configured with all necessary vars
- Fallback values for non-critical vars
Step 6: Build Validation
# Clean build
npm run build
# Check for build errors
echo $? # Should be 0
# Verify dist/ directory created
ls -la dist/
Expected Results:
- Build succeeds without errors
- dist/ directory contains compiled JavaScript
- No TypeScript errors
Step 7: Local Production Simulation
# Run in production mode locally
NODE_ENV=production npm start
# Test critical endpoints
curl http://localhost:3000/health
curl http://localhost:3000/demo-scenario-picker.html
curl http://localhost:3000/api/v1/requirements
Expected Results:
- Server starts successfully
- All endpoints return 200 (not 404)
- HTML files serve correctly
Automated Validation Script
Create this script at /scripts/validate-deployment.sh:
#!/bin/bash
# Deployment Validation Script
# Run this BEFORE every deployment
set -e # Exit on first error
echo "=== Project Conductor Deployment Validator ==="
echo ""
# Step 1: Git Status
echo "1. Checking git status..."
UNTRACKED=$(git status --porcelain | grep "^??" || true)
if [ -n "$UNTRACKED" ]; then
echo "❌ FAIL: Untracked files found:"
echo "$UNTRACKED"
exit 1
fi
echo "✅ PASS: No untracked files"
UNCOMMITTED=$(git status --porcelain | grep "^ M" || true)
if [ -n "$UNCOMMITTED" ]; then
echo "⚠️ WARNING: Uncommitted changes found:"
echo "$UNCOMMITTED"
read -p "Continue anyway? (y/n) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 1
fi
fi
# Step 2: Check for duplicate routes
echo ""
echo "2. Checking for duplicate routes..."
DUPLICATES=$(grep -n "app.get.*\.html" src/index.ts | wc -l)
if [ "$DUPLICATES" -gt 5 ]; then
echo "⚠️ WARNING: Found $DUPLICATES explicit HTML routes"
echo " Review for conflicts with static middleware"
grep -n "app.get.*\.html" src/index.ts
fi
# Step 3: Check for hardcoded paths
echo ""
echo "3. Checking for hardcoded paths..."
HARDCODED=$(grep -r "\/Users\/" src/ 2>/dev/null | grep -v node_modules || true)
if [ -n "$HARDCODED" ]; then
echo "❌ FAIL: Hardcoded paths found:"
echo "$HARDCODED"
exit 1
fi
echo "✅ PASS: No hardcoded paths"
# Step 4: Validate static file configuration
echo ""
echo "4. Validating static file configuration..."
PUBLIC_FILES=$(ls -1 public/*.html 2>/dev/null | wc -l)
echo " Found $PUBLIC_FILES HTML files in /public"
# Check if publicDir is used correctly
PUBLICDIR_USAGE=$(grep -c "express.static(publicDir)" src/index.ts || true)
if [ "$PUBLICDIR_USAGE" -lt 1 ]; then
echo "⚠️ WARNING: publicDir static middleware not found"
fi
# Step 5: Build test
echo ""
echo "5. Running build test..."
npm run build > /dev/null 2>&1
if [ $? -eq 0 ]; then
echo "✅ PASS: Build successful"
else
echo "❌ FAIL: Build failed"
exit 1
fi
# Step 6: Check required files
echo ""
echo "6. Checking required files..."
REQUIRED_FILES=(
"src/index.ts"
"package.json"
"tsconfig.json"
)
for file in "${REQUIRED_FILES[@]}"; do
if [ ! -f "$file" ]; then
echo "❌ FAIL: Missing required file: $file"
exit 1
fi
done
echo "✅ PASS: All required files present"
# Summary
echo ""
echo "=== Deployment Validation Complete ==="
echo "✅ Ready to deploy to Render"
echo ""
echo "Next steps:"
echo " 1. git add ."
echo " 2. git commit -m 'Ready for deployment'"
echo " 3. git push origin main"
echo " 4. Monitor Render deployment logs"
Make it executable:
chmod +x scripts/validate-deployment.sh
Usage
Automatic Invocation
When user says: "ready to deploy", "push to render", "deploy to production"
Your Response:
🛡️ Running deployment validation first...
[Run validation checklist steps 1-7]
Results:
✅ Static files validated
✅ Route precedence correct
✅ No hardcoded paths
✅ Git status clean
✅ Environment variables documented
✅ Build successful
✅ Local production test passed
All checks passed! Ready to deploy.
Manual Invocation
User can explicitly call:
npm run validate:deploy
# or
./scripts/validate-deployment.sh
Fix Patterns Reference
Pattern 1: File in /public, served from projectRoot
Detection: File exists in /public but 404 in production Fix:
// Before (WRONG)
app.get('/file.html', (req, res) => {
res.sendFile(path.join(projectRoot, 'file.html')); // File not here!
});
// After (CORRECT)
// Remove explicit route, let static middleware handle it
app.use(express.static(publicDir)); // This serves /public files at root
Pattern 2: Explicit route overrides static middleware
Detection: grep shows both static middleware AND explicit route for same file Fix:
// Before (CONFLICT)
app.use(express.static(publicDir)); // Line 199
app.get('/file.html', ...); // Line 450 - OVERRIDES!
// After (RESOLVED)
app.use(express.static(publicDir)); // Line 199
// Removed explicit route - static middleware handles it
Pattern 3: File moved but not tracked
Detection: git status shows ?? public/file.html
Fix:
git add public/file.html
git commit -m "Add file to public directory"
git push origin main
Pattern 4: Wrong path in sendFile
Detection: ENOENT error in production logs Fix:
// Before (WRONG)
res.sendFile(path.join(projectRoot, 'file.html')); // File is in /public!
// After (CORRECT)
res.sendFile(path.join(publicDir, 'file.html')); // Correct base dir
Integration with package.json
Add these scripts:
{
"scripts": {
"validate:deploy": "./scripts/validate-deployment.sh",
"predeploy": "npm run validate:deploy",
"deploy": "git push origin main"
}
}
Now npm run deploy automatically validates before pushing.
Skill Improvement Tracking
Version History:
- 1.0.0 (2025-10-19): Initial creation from deployment debugging session
- Captured static file path mismatch issue
- Captured route precedence issue
- Captured git tracking issue
- Created automated validation checklist
Future Enhancements:
- Add automated fix suggestions (not just detection)
- Integrate with CI/CD pipeline
- Add Render-specific log monitoring
- Database migration validation
- Environment variable auto-sync with Render
Success Metrics
This skill is successful if:
- Zero "works locally, fails on Render" incidents after deployment
- Validation catches issues before git push (not after)
- New developers can deploy confidently using this checklist
- Production deployments succeed on first try (no rollbacks)
Related Skills
validation: General validation workflow (tests, linting)scout: Find deployment best practices from external sources
References
- Express.js static middleware docs: https://expressjs.com/en/starter/static-files.html
- Render deployment guide: https://render.com/docs/deploy-node-express-app
- Path module docs: https://nodejs.org/api/path.html
Remember: This skill was created from REAL debugging pain. Every check in this list prevented an actual production issue. Use it religiously.
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?