| name | dev-server-lifecycle-management |
| description | Standardized patterns for detecting, starting, monitoring, and maintaining development server connections. Use when connecting to dev servers, managing server lifecycle, or when connection losses occur during browser automation. Eliminates 2-3 minutes of server management overhead per task. |
Dev Server Lifecycle Management
Overview
Standardized patterns for detecting, starting, monitoring, and maintaining development server connections. Prevents connection losses and reduces server management overhead.
Problem: Connection losses during browser automation cause retries and delays, port detection requires multiple attempts, no health checks before critical browser operations, server restart logic is inconsistent.
Solution: Port detection strategy, health check patterns, connection resilience, startup verification.
Impact: Eliminate 2-3 minutes of server management overhead per task, reduce connection-related failures.
Port Detection Strategy
Pattern 1: Check Configuration Files
Check common ports or read from config files (package.json, vite.config, etc.):
const viteConfig = read_file('vite.config.ts');
const port = extractPortFromViteConfig(viteConfig);
const packageJson = read_file('package.json');
const port = extractPortFromPackageJson(packageJson);
Pattern 2: Use Process Inspection
Use lsof or process inspection to find running dev servers:
lsof -i :3000
lsof -i :5173
lsof -i :8080
ps aux | grep -E "(vite|node|npm)" | grep -v grep
Pattern 3: Cache Port Information
Cache port information for subsequent operations:
let cachedPort: number | null = null;
function getDevServerPort(): number {
if (cachedPort) {
return cachedPort;
}
cachedPort = detectPort();
return cachedPort;
}
Health Check Patterns
Pattern 1: Verify HTTP Response
Verify server responds to HTTP requests before browser operations:
curl -f http://localhost:5173 > /dev/null 2>&1
if [ $? -eq 0 ]; then
echo "Server is ready"
else
echo "Server not ready"
fi
Pattern 2: Check DOM Elements
Check for specific DOM elements or API endpoints indicating readiness:
agent-browser open "http://localhost:5173"
agent-browser eval "
const ready = document.querySelector('#app') !== null;
ready
"
curl -f http://localhost:5173/api/health
Pattern 3: Exponential Backoff
Implement exponential backoff for readiness checks (1s, 2s, 4s):
async function waitForServerReady(url: string, maxAttempts = 5): Promise<boolean> {
for (let attempt = 0; attempt < maxAttempts; attempt++) {
const delay = Math.pow(2, attempt) * 1000;
await sleep(delay);
const isReady = await checkServerHealth(url);
if (isReady) {
return true;
}
}
return false;
}
Connection Resilience
Pattern 1: Track Server State
Track dev server PID/port in agent state:
const serverState = {
pid: null as number | null,
port: null as number | null,
startTime: null as Date | null,
};
function startDevServer(): void {
const process = spawn('npm', ['run', 'dev']);
serverState.pid = process.pid;
serverState.port = detectPort();
serverState.startTime = new Date();
}
Pattern 2: Automatic Restart
Implement automatic restart on connection failure:
async function ensureServerRunning(): Promise<void> {
const isRunning = await checkServerHealth(`http://localhost:${serverState.port}`);
if (!isRunning) {
console.log('Server not running, restarting...');
stopDevServer();
startDevServer();
await waitForServerReady(`http://localhost:${serverState.port}`);
}
}
Pattern 3: Cache Browser Session State
Cache browser session state to resume after connection loss:
const browserState = {
url: null as string | null,
viewport: null as { width: number; height: number } | null,
cookies: null as any,
};
function saveBrowserState(): void {
browserState.url = getCurrentUrl();
browserState.viewport = getViewport();
browserState.cookies = getCookies();
}
function restoreBrowserState(): void {
if (browserState.url) {
agent-browser open browserState.url;
agent-browser viewport browserState.viewport.width browserState.viewport.height;
}
}
Startup Verification
Pattern 1: Wait for Full Readiness
Wait for server to be fully ready (not just started):
async function startDevServerWithVerification(): Promise<void> {
startDevServer();
const isReady = await waitForServerReady(`http://localhost:${serverState.port}`);
if (!isReady) {
throw new Error('Server failed to become ready');
}
console.log('Server is ready');
}
Pattern 2: Verify Test Seam Commands
Verify test seam commands are available before proceeding:
agent-browser open "http://localhost:5173?scene=GameScene"
agent-browser eval "
const hasTestSeam = typeof window.__TEST__ !== 'undefined' &&
typeof window.__TEST__.commands !== 'undefined';
hasTestSeam
"
for i in {1..5}; do
if agent-browser eval "typeof window.__TEST__ !== 'undefined'"; then
echo "Test seam ready"
break
fi
sleep $((2 ** $i))
done
Pattern 3: Event-Based Readiness Checks
Use event-based readiness checks instead of fixed polling:
function waitForServerReadyEvent(url: string): Promise<void> {
return new Promise((resolve, reject) => {
const maxWait = 30000;
const startTime = Date.now();
const checkInterval = setInterval(async () => {
if (Date.now() - startTime > maxWait) {
clearInterval(checkInterval);
reject(new Error('Server readiness timeout'));
return;
}
const isReady = await checkServerHealth(url);
if (isReady) {
clearInterval(checkInterval);
resolve();
}
}, 1000);
});
}
Hot Module Replacement (HMR) Verification Patterns
Verify HMR Has Applied Changes
After code changes, verify HMR has applied changes before testing:
agent-browser eval "
const lastReload = window.__HMR_LAST_RELOAD__ || 0;
const now = Date.now();
const timeSinceReload = now - lastReload;
timeSinceReload < 5000 ? 'Recently reloaded' : 'Stale state'
"
agent-browser eval "
window.__TEST__?.ready ? 'Ready' : 'Not ready'
"
agent-browser screenshot screenshots/verification.png
HMR Verification Patterns
Pattern 1: Check HMR Status
agent-browser eval "
if (import.meta.hot) {
'HMR available'
} else {
'HMR not available'
}
"
Pattern 2: Wait for HMR Completion
wait_for_hmr() {
local max_wait=20
local elapsed=0
while [ $elapsed -lt $max_wait ]; do
if agent-browser eval "window.__HMR_LAST_RELOAD__ && (Date.now() - window.__HMR_LAST_RELOAD__) < 5000"; then
echo "HMR applied"
return 0
fi
sleep 1
elapsed=$((elapsed + 1))
done
echo "HMR timeout"
return 1
}
Pattern 3: Verify HMR Before Testing
async function verifyHMRBeforeTesting(url: string): Promise<boolean> {
const hasHMR = await checkHMRStatus(url);
if (!hasHMR) {
console.warn('HMR not available, forcing refresh');
await forceBrowserRefresh(url);
return true;
}
const hmrApplied = await waitForHMRCompletion(url);
if (!hmrApplied) {
console.warn('HMR timeout, forcing refresh');
await forceBrowserRefresh(url);
return true;
}
const changesApplied = await verifyChangesApplied(url);
return changesApplied;
}
How to Wait for HMR Completion Before Testing
Workflow for HMR verification:
-
Wait for compilation success:
-
Verify browser has reloaded:
agent-browser eval "
const lastReload = window.__HMR_LAST_RELOAD__ || 0;
const now = Date.now();
const timeSinceReload = now - lastReload;
timeSinceReload < 5000 ? 'Recently reloaded' : 'Stale state'
"
-
Wait for scene/component initialization:
agent-browser eval "
window.__TEST__?.ready ? 'Ready' : 'Not ready'
"
-
Then capture screenshots or run tests:
agent-browser screenshot screenshots/verification.png
Fallback When HMR Isn't Available
If HMR isn't working:
-
Force browser refresh:
agent-browser reload
agent-browser wait 5000
-
Restart dev server if needed:
pkill -f "vite\|npm.*dev"
npm run dev &
sleep 5
-
Verify changes are in codebase:
grep -r "expected change" src/
HMR Verification in Complete Workflow
Add HMR verification to server startup workflow:
const port = getDevServerPort();
if (!isServerRunning(port)) {
startDevServer();
await waitForServerReady(`http://localhost:${port}`);
}
const isHealthy = await checkServerHealth(`http://localhost:${port}`);
if (!isHealthy) {
throw new Error('Server health check failed');
}
const hasHMR = await verifyHMRStatus(`http://localhost:${port}`);
if (hasHMR) {
console.log('HMR available');
} else {
console.warn('HMR not available, will use full refresh');
}
const hasTestSeam = await verifyTestSeam(`http://localhost:${port}`);
if (!hasTestSeam) {
console.warn('Test seam not available, proceeding anyway');
}
serverState.port = port;
serverState.startTime = new Date();
serverState.hmrAvailable = hasHMR;
agent-browser open `http://localhost:${port}?scene=GameScene`;
Complete Workflow Example
Example: Start and Verify Dev Server
const port = getDevServerPort();
if (!isServerRunning(port)) {
startDevServer();
await waitForServerReady(`http://localhost:${port}`);
}
const isHealthy = await checkServerHealth(`http://localhost:${port}`);
if (!isHealthy) {
throw new Error('Server health check failed');
}
const hasTestSeam = await verifyTestSeam(`http://localhost:${port}`);
if (!hasTestSeam) {
console.warn('Test seam not available, proceeding anyway');
}
serverState.port = port;
serverState.startTime = new Date();
agent-browser open `http://localhost:${port}?scene=GameScene`;
Best Practices
- Detect port first: Check config files before assuming defaults
- Health check: Verify server responds before browser operations
- Exponential backoff: Use 1s, 2s, 4s delays for readiness checks
- Cache port: Store port for session reuse
- Track state: Monitor PID/port in agent state
- Auto-restart: Restart on connection failure
- Verify readiness: Wait for full server readiness
- Event-based checks: Use event-based instead of fixed polling
- Verify HMR: Check HMR status and wait for completion before testing
- Fallback to refresh: Force browser refresh if HMR isn't available
Common Pitfalls
Pitfall 1: Assuming Default Port
Problem: Assuming port 3000 without checking
Solution: Check config files first
agent-browser open "http://localhost:3000"
PORT=$(detect_port.sh)
agent-browser open "http://localhost:$PORT"
Pitfall 2: No Health Check
Problem: Opening browser before server is ready
Solution: Health check before browser operations
agent-browser open "http://localhost:5173"
curl -f http://localhost:5173 > /dev/null 2>&1
if [ $? -eq 0 ]; then
agent-browser open "http://localhost:5173"
fi
Pitfall 3: Fixed Polling
Problem: Using fixed polling intervals
Solution: Use exponential backoff
for (let i = 0; i < 10; i++) {
await sleep(1000);
if (isReady()) break;
}
for (let i = 0; i < 5; i++) {
await sleep(Math.pow(2, i) * 1000);
if (isReady()) break;
}
Integration with Other Skills
dev-server-port-detection: Uses port detection patterns
agent-browser: Coordinates with browser automation
phaser-game-testing: Verifies test seam availability
Related Skills
dev-server-port-detection - Port detection strategies
agent-browser - Browser automation patterns
phaser-game-testing - Test seam verification
Remember
- Detect port: Check config files before assuming defaults
- Health check: Verify server responds before browser operations
- Exponential backoff: Use 1s, 2s, 4s for readiness checks
- Cache port: Store for session reuse
- Track state: Monitor PID/port in agent state
- Auto-restart: Restart on connection failure
- Verify readiness: Wait for full server readiness
- Event-based: Use event-based instead of fixed polling