From Dev Process
Replaces arbitrary test timeouts with condition polling to fix flaky tests with race conditions, timing dependencies, or inconsistent pass/fail behavior.
How this skill is triggered — by the user, by Claude, or both
Slash command
/dev-process:condition-based-waitingThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Flaky tests often guess at timing with arbitrary delays. This creates race conditions where tests pass on fast machines but fail under load or in CI.
Flaky tests often guess at timing with arbitrary delays. This creates race conditions where tests pass on fast machines but fail under load or in CI.
Core principle: Wait for the actual condition you care about, not a guess about how long it takes.
Use when:
setTimeout, sleep, time.sleep())Don't use when:
// ❌ BEFORE: Guessing at timing
await new Promise(r => setTimeout(r, 50));
const result = getResult();
expect(result).toBeDefined();
// ✅ AFTER: Waiting for condition
await waitFor(() => getResult() !== undefined);
const result = getResult();
expect(result).toBeDefined();
| Scenario | Pattern |
|---|---|
| Wait for event | waitFor(() => events.find(e => e.type === 'DONE')) |
| Wait for state | waitFor(() => machine.state === 'ready') |
| Wait for count | waitFor(() => items.length >= 5) |
| Wait for file | waitFor(() => fs.existsSync(path)) |
| Complex condition | waitFor(() => obj.ready && obj.value > 10) |
async function waitFor<T>(
condition: () => T | undefined | null | false,
description: string,
timeoutMs = 5000
): Promise<T> {
const startTime = Date.now();
while (true) {
const result = condition();
if (result) return result;
if (Date.now() - startTime > timeoutMs) {
throw new Error(`Timeout waiting for ${description} after ${timeoutMs}ms`);
}
await new Promise(r => setTimeout(r, 10)); // Poll every 10ms
}
}
import time
def wait_for(condition, description, timeout=5.0, poll_interval=0.01):
start = time.time()
while True:
result = condition()
if result:
return result
if time.time() - start > timeout:
raise TimeoutError(f"Timeout waiting for {description}")
time.sleep(poll_interval)
| Mistake | Fix |
|---|---|
| Polling too fast (1ms) | Poll every 10ms |
| No timeout | Always include timeout with clear error |
| Stale data (cache before loop) | Call getter inside loop for fresh data |
| Vague error message | Include what was expected |
// Tool ticks every 100ms - need 2 ticks to verify partial output
await waitForEvent(manager, 'TOOL_STARTED'); // First: wait for condition
await new Promise(r => setTimeout(r, 200)); // Then: wait for timed behavior
// 200ms = 2 ticks at 100ms intervals - documented and justified
Requirements:
From debugging session:
npx claudepluginhub jhamidun/claude-code-config-pack --plugin dev-processBuilds a throwaway prototype to answer a design question about UI appearance or state/logic behavior. Guides you through two branches: interactive terminal app for logic validation, or multiple UI variations for visual exploration.