Skip to main content
skill CX Codex

condition-based-waiting — Claude Code skill: setup and details

Replaces hardcoded timeouts like sleep or setTimeout with condition polling in async tests. Eliminates flakiness caused by execution timing.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add bobmatnyc/claude-mpm-skills --skill condition-based-waiting -a claude-code
Open on GitHub

What condition-based-waiting does

This skill guides the coding assistant to refactor async test suites. Instead of arbitrary delays like sleep() or time.sleep(), it applies a generic waitFor polling loop. The function checks a condition every 10ms until it resolves or hits a 5000ms timeout threshold.

It includes patterns for event streams and file creation checks. The instructions provide specialized helpers such as waitForEvent and waitForEventCount. Tests stop failing on busy systems.

Do not apply this pattern when testing debounce or throttle behavior. In those cases, verifying time intervals is part of the application contract.

What it helps with

  • eliminate flaky test failures under CI load
  • replace fixed sleep calls with predicate polling
  • prevent race conditions in parallel test runs
  • wait for file or event readiness reliably

Example prompts for the agent

  • Refactor sleep calls in this test file to use condition polling
  • Rewrite this flaky event test with a waitFor helper
  • Replace fixed timeouts with condition-based waiting in async tests

How to install condition-based-waiting

1Skills CLI (Vercel)

npx skills add bobmatnyc/claude-mpm-skills --skill condition-based-waiting -a claude-code

2Manually: put the files in place

~/.claude/skills/condition-based-waiting/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/condition-based-waiting/

Step by step, with commands for each platform: How to install skills in Claude Code: five ways

Questions about condition-based-waiting

How to install condition-based-waiting in Claude Code?

Run npx skills add bobmatnyc/claude-mpm-skills --skill condition-based-waiting -a claude-code. The skill file installs to ~/.claude/skills/condition-based-waiting/SKILL.md.

What does condition-based-waiting do?

It provides guidelines to rewrite arbitrary delays into polling loops that check for exact conditions. This stops async tests from failing randomly on busy environments.

When should arbitrary timeouts be kept?

Keep them when directly testing timing constraints like debounce and throttle intervals. In those tests, measuring real delay durations is the intended target.

Source text

SKILL.md · MIT
---
name: condition-based-waiting
description: Replace arbitrary timeouts with condition polling for reliable async tests
user-invocable: false
disable-model-invocation: true
when_to_use: when tests have race conditions, timing dependencies, or inconsistent pass/fail behavior
version: 1.1.0
languages: all
progressive_disclosure:
  entry_point:
    summary: "Replace arbitrary timeouts with condition polling for reliable async tests"
    when_to_use: "Tests with setTimeout/sleep, flaky tests, or timing-dependent async operations"
    quick_start: |
      1. Identify arbitrary delays in tests (setTimeout, sleep, time.sleep())
      2. Replace with condition-based waiting (waitFor pattern)
      3. Use domain-specific helpers for common scenarios
      See @example.ts for complete working implementation
    core_pattern: |
      // ❌ Guessing at timing
      await new Promise(r => setTimeout(r, 50));

      // ✅ Waiting for condition
      await waitFor(() => getResult() !== undefined);
  references:
    - path: references/patterns-and-implementation.md
      purpose: Detailed waiting patterns, implementation guide, and common mistakes
      when_to_read: When implementing waitFor or debugging timing issues
---

# Condition-Based Waiting

## Overview

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.

## When to Use

```dot
digraph when_to_use {
    "Test uses setTimeout/sleep?" [shape=diamond];
    "Testing timing behavior?" [shape=diamond];
    "Document WHY timeout needed" [shape=box];
    "Use condition-based waiting" [shape=box];

    "Test uses setTimeout/sleep?" -> "Testing timing behavior?" [label="yes"];
    "Testing timing behavior?" -> "Document WHY timeout needed" [label="yes"];
    "Testing timing behavior?" -> "Use condition-based waiting" [label="no"];
}
```

**Use when:**
- Tests have arbitrary delays (`setTimeout`, `sleep`, `time.sleep()`)
- Tests are flaky (pass sometimes, fail under load)
- Tests timeout when run in parallel
- Waiting for async operations to complete

**Don't use when:**
- Testing actual timing behavior (debounce, throttle intervals)
- Always document WHY if using arbitrary timeout

## Core Pattern

```typescript
// ❌ 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();
```

## Quick Patterns

| 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)` |

## Implementation

Generic polling function:
```typescript
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
  }
}
```

See @example.ts for complete implementation with domain-specific helpers (`waitForEvent`, `waitForEventCount`, `waitForEventMatch`).

For detailed patterns, implementation guide, and common mistakes, see @references/patterns-and-implementation.md

## Real-World Impact

From debugging session (2025-10-03):
- Fixed 15 flaky tests across 3 files
- Pass rate: 60% → 100%
- Execution time: 40% faster
- No more race conditions

© bobmatnyc · MIT · bobmatnyc/claude-mpm-skills

Similar skills

All skills for Testing and QA →

From the same set125

vercel-overview A reference skill for the Vercel platform: projects, CLI, environment variables, Git integration, REST API and webhooks. Useful when an agent plans a deploy or digs into project settings. CX Codex +1 328installs vercel-functions-runtime Provides patterns for Vercel Functions, Edge Runtime, routing middleware, and cron jobs. Useful when designing serverless APIs or scheduled tasks. CX Codex +1 328installs flexlayout-react A skill that walks your coding agent through flexlayout-react: draggable panels, tabs, splitters and saved layouts for React apps. CX Codex +1 328installs vercel-security-access A reference skill for Vercel security: roles, SSO, deployment protection, firewall, BotID and audit logs. Useful when you lock down projects and previews. CX Codex +1 328installs vercel-ai Architecture guidance for Vercel AI SDK, AI Gateway, and MCP workflows. Helps when building agentic workloads on Vercel. CX Codex +1 324installs digitalocean-agentic-cloud Guides your coding assistant through building and deploying AI agents on DigitalOcean Gradient AI. Useful for managing GPU resources, knowledge bases, and agent routing. CX Codex +1 323installs validated-handler Adds a type-safe route handler pattern for Next.js App Router using Zod validation. It cuts repetitive parsing and keeps API errors consistent. CX Codex +1 322installs terraform This skill structures Terraform work around state, plans, modules, and CI checks. Use it when infrastructure changes need reviewable steps. CX Codex +1 322installs

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor