cf-tdd

Medium~1K–2.5K tokens injected into prompt

Enforces test-driven development: RED, GREEN, REFACTOR.

Context footprint: ⚡⚡ (medium) — what does this mean?

The cf-tdd skill automatically activates when you're writing new code or refactoring existing code. It enforces the test-driven development (TDD) discipline: write a failing test first, implement minimal code to pass it, then refactor.

Skip Conditions

TDD is automatically skipped when:

  • All changed files are non-code — styles (.css, .scss, .sass, .less, .styl), docs (.md, .mdx, .txt, .rst), or config (.json, .yaml, .yml, .toml, .env, .ini, .lock, .config). Whitelist matches only when all changed files belong to these types — mixing in a .ts file (or any other code file) re-enables TDD for the whole change.
  • User passes --no-tdd — explicitly opts out with a one-line acknowledgment. This flag is recognized by the cf-tdd skill itself. Passing it through /cf-fix or /cf-plan today does not propagate to the downstream cf-implementer agent; those skills always delegate to the strict TDD implementer regardless of the flag.

When skipped, the skill notes why and proceeds directly to implementation.

Agent Handoff

When cf-tdd dispatches the cf-implementer agent for substantial implementations, it uses a structured JSON context file to pass findings without losing nuance through text serialization. See Agent Context Handoff for the full mechanism, schema, and retry protocol.

When It Activates

Automatically triggered when:

  • Creating new functions or methods
  • Adding new modules or classes
  • Writing feature code from scratch
  • Refactoring existing code ("refactor this", "clean up", "extract this", "simplify")
  • Test file exists but implementation doesn't

The TDD Cycle

RED: Write Failing Test

Start by writing a test that describes desired behavior—it fails because the code doesn't exist yet.

// test/user.test.ts
test("should hash password with bcrypt", () => {
  const password = "secret123";
  const hashed = hashPassword(password);
  expect(hashed).not.toBe(password);
  expect(hashed.length).toBeGreaterThan(10);
});

GREEN: Implement Minimal Code

Write the simplest code that makes the test pass. No over-engineering.

// src/user.ts
export function hashPassword(password: string): string {
  // Minimal implementation to pass test
  return "hashed_" + password;
}

REFACTOR: Improve Code

Now improve the code while keeping tests green.

// src/user.ts
import bcrypt from "bcrypt";

export function hashPassword(password: string): string {
  return bcrypt.hashSync(password, 10);
}

Benefits

  • Confidence — Tests ensure code works before production
  • Design — Writing tests first leads to better API design
  • Coverage — Most code has tests automatically
  • Refactoring — Safe to improve code with test safety net
  • Documentation — Tests serve as usage examples

When It Helps Most

  • Complex business logic
  • Data transformations
  • Critical paths (auth, payments)
  • Edge case handling
  • Error scenarios

Key Rules

  1. Always test first — No code without failing test
  2. Keep tests simple — Each test validates one behavior
  3. One assert per test — Clear pass/fail indication
  4. Test edge cases — Null, empty, invalid input
  5. Refactor freely — Tests prevent regressions