arielperez82

tpp

0
0
# Install this skill:
npx skills add arielperez82/agents-and-skills --skill "tpp"

Install specific skill from multi-skill repository

# Description

>

# SKILL.md


name: tpp
description: >
Provides reference material on the Transformation Priority Premise (TPP) for Test-Driven Development.
Contains transformation catalog, priority ordering, and methodology for avoiding TDD impasses.
Load when learning about transformations or reviewing TPP principles.


Transformation Priority Premise (TPP)

Core Insight: "As the tests get more specific, the code gets more generic."

The Transformation Priority Premise provides a methodology for choosing which tests to write and how to implement them during TDD's RED-GREEN cycle.

What Are Transformations?

Transformations change the behavior of code, moving it from specific to generic forms.
Refactorings change the structure of code without changing behavior.

During TDD:
- RED→GREEN: Use transformations (change behavior to pass test)
- GREEN→REFACTOR: Use refactorings (improve structure, preserve behavior)


The Transformation Catalog

Listed from simplest (top) to most complex (bottom):

# Transformation Description Example
1 ({}→nil) No code → code that returns nil return null;
2 (nil→constant) nil → a constant value return "";
3 (constant→constant+) Simple constant → more complex constant return 0;return 42;
4 (constant→scalar) Constant → variable/argument return 0;return score;
5 (statement→statements) One statement → multiple statements Add more unconditional logic
6 (unconditional→if) Unconditional code → conditional split Add if statement
7 (scalar→array) Single value → collection let x = 5;let x = [5];
8 (array→container) Array → more complex structure Array → Map/Set/Object
9 (statement→recursion) Statement → recursive call x++;fn(x+1);
10 (if→while) Conditional → loop if (...)while (...)
11 (expression→function) Expression → algorithm/function x + ycalculate(x, y)
12 (variable→assignment) Immutable → mutable state const x = 5;let x = 5; x++;

The Priority Premise

Prefer transformations higher in the list (simpler) over those lower (more complex).

Why Priority Matters

  1. Simpler transformations = smaller steps
  2. Smaller steps = easier to keep tests green
  3. Wrong order = impasses (stuck, can't proceed without rewriting)

The Rule

When in GREEN phase:
- Try to pass the test using the highest priority transformation possible
- If forced to use low-priority transformation, consider if there's a simpler test

When in RED phase:
- Choose tests that can be passed with high-priority transformations
- Defer tests requiring complex transformations until simpler ones establish patterns


Transformation Examples

1. (nil→constant) - Simplest Start

// Test (RED)
it('returns empty string for null input', () => {
  expect(wrap(null, 10)).toBe('');
});

// Implementation (GREEN)
function wrap(s: string | null, length: number): string {
  return '';  // (nil→constant)
}

2. (constant→scalar) - Introduce Variable

// Test (RED)
it('returns the input string unchanged', () => {
  expect(wrap('word', 10)).toBe('word');
});

// Implementation (GREEN)
function wrap(s: string | null, length: number): string {
  if (s === null) return '';
  return s;  // (constant→scalar) - was '', now variable s
}

3. (unconditional→if) - Split Execution Path

// Test (RED)
it('breaks long words at the length limit', () => {
  expect(wrap('longword', 4)).toBe('long\nword');
});

// Implementation (GREEN)
function wrap(s: string | null, length: number): string {
  if (s === null) return '';

  if (s.length <= length)  // (unconditional→if)
    return s;
  else
    return 'long\nword';  // (nil→constant) in new branch
}

4. (constant→scalar) Again - Remove Hard-Coded Value

// Test (RED) - Add second assertion to force generalization
it('breaks long words at the length limit', () => {
  expect(wrap('longword', 4)).toBe('long\nword');
  expect(wrap('longerword', 6)).toBe('longer\nword');
});

// Implementation (GREEN)
function wrap(s: string | null, length: number): string {
  if (s === null) return '';

  if (s.length <= length)
    return s;
  else
    return s.substring(0, length) + '\n' + s.substring(length);  // (constant→scalar)
}

5. (statement→recursion) - Handle Multiple Breaks

// Test (RED)
it('breaks very long words multiple times', () => {
  expect(wrap('verylongword', 4)).toBe('very\nlong\nword');
});

// Implementation (GREEN)
function wrap(s: string | null, length: number): string {
  if (s === null) return '';

  if (s.length <= length)
    return s;
  else
    // (statement→recursion) - recurse on remainder
    return s.substring(0, length) + '\n' + wrap(s.substring(length), length);
}

Avoiding Impasses: The Word Wrap Kata

The Wrong Path (Leads to Impasse)

// Early test using low-priority transformation
it('wraps two words at space', () => {
  expect(wrap('word word', 6)).toBe('word\nword');
});

// "Clever" solution using (expression→function) - too early!
function wrap(s: string | null, length: number): string {
  if (s === null) return '';
  return s.replaceAll(' ', '\n');  // Breaks for next test!
}

// Next test reveals the impasse
it('wraps three words correctly', () => {
  expect(wrap('word word word', 9)).toBe('word word\nword');
  // Can't pass without major rewrite!
});

Problem: Used low-priority transformation (expression→function) too early, before establishing the core algorithm with simpler transformations.

The Right Path (Following TPP)

// 1. Start with simpler test (breaking long words)
it('breaks long words at length', () => {
  expect(wrap('longword', 4)).toBe('long\nword');
});
// Pass with: (unconditional→if) + (constant→scalar)

// 2. Generalize with recursion
it('breaks very long words multiple times', () => {
  expect(wrap('verylongword', 4)).toBe('very\nlong\nword');
});
// Pass with: (statement→recursion)

// 3. NOW handle spaces (builds on existing algorithm)
it('wraps at space when possible', () => {
  expect(wrap('word word', 6)).toBe('word\nword');
});
// Pass with: (unconditional→if) - fits naturally into existing structure

Why this works: Each transformation builds on the previous, establishing patterns that accommodate future tests.


Transformation Priority in Practice

Decision Framework

When choosing next test:

  1. Can I pass with (nil→constant) or (constant→scalar)?
  2. These are always preferred
  3. Build foundation incrementally

  4. Will this require (unconditional→if)?

  5. Acceptable if no simpler option
  6. Try to defer until patterns emerge

  7. Will this require (expression→function) or lower?

  8. ⚠️ Warning sign - might be too early
  9. Ask: "Is there a simpler test I could write first?"

  10. Will this require complete algorithm redesign?

  11. 🚫 Definitely wrong order
  12. Backtrack and find intermediate tests

Red Flags

Signs you're on wrong path:
- Can't pass test without rewriting existing code
- Forced to use transformation from bottom of list
- "Simple" test requires complex implementation
- Previous tests become harder to understand

Solution: Backtrack. Find simpler test that uses higher-priority transformation.


TPP and Code Evolution

The Pattern

Start: Specific, correct for one case
  ↓  (transformation)
More generic, correct for more cases
  ↓  (transformation)
Even more generic, correct for all cases

Example: Scoring Algorithm Evolution

// Test 1: Gutter game (all zeros)
score() { return 0; }  // (nil→constant)

// Test 2: All ones
score() { return rolls.reduce((a, b) => a + b); }  // (constant→scalar)

// Test 3: Spare handling
score() { 
  let score = 0;
  for (let i = 0; i < rolls.length; i++) {  // (scalar→array) + (statement→statements)
    score += rolls[i];
    if (isSpare(i)) score += rolls[i + 1];
  }
  return score;
}
// (unconditional→if)

// Algorithm emerged through transformations, not designed upfront

Common Transformation Sequences

Building Collections

(nil→constant)           return null
  ↓
(constant→scalar)        return []
  ↓
(scalar→array)          return [item]
  ↓
(statement→statements)   return [item1, item2]
  ↓
(expression→function)    return items.map(transform)

Building Conditional Logic

(nil→constant)           return false
  ↓
(constant→scalar)        return isValid
  ↓
(unconditional→if)       if (condition) return true; return false;
  ↓
(if→while)              while (condition) { ... }

Building Calculations

(nil→constant)           return 0
  ↓
(constant→scalar)        return value
  ↓
(statement→statements)   let x = value; return x * 2;
  ↓
(expression→function)    return calculate(value)

TPP and Test Selection

Good Test Progression (Following TPP)

// 1. Degenerate case
it('returns empty for null', () => { ... });           // (nil→constant)

// 2. Simplest real case  
it('returns unchanged when short', () => { ... });     // (constant→scalar)

// 3. Simple boundary
it('breaks at exact length', () => { ... });           // (unconditional→if)

// 4. Multiple occurrences
it('breaks multiple times', () => { ... });            // (statement→recursion)

// 5. Complex case builds on foundation
it('prefers breaking at spaces', () => { ... });       // (unconditional→if) in recursion

Poor Test Progression (Ignoring TPP)

// 1. Degenerate case
it('returns empty for null', () => { ... });           // (nil→constant)

// 2. JUMP TO COMPLEX CASE - BAD!
it('wraps multiple words at spaces', () => { ... });   // Requires (expression→function)!

// Now stuck - can't build incrementally

Key Principles

1. Transformations Change Behavior

During GREEN phase, you're changing what the code does to satisfy the test.

2. Simpler is Better

Higher-priority transformations = smaller, safer steps.

3. Tests Drive Transformations

The test you choose determines which transformation you'll need.

4. Generalization Over Time

Don't try to write the general solution immediately. Let it emerge through transformations.

5. Impasses Signal Wrong Order

If stuck, you probably chose tests in wrong order. Backtrack.


When to Apply TPP

Use TPP when:
- ✅ Choosing which test to write next
- ✅ Deciding how to make a test pass
- ✅ Feeling stuck during GREEN phase
- ✅ Code seems to require major rewrite for simple test

Don't overthink TPP when:
- ❌ Transformation choice is obvious
- ❌ Only one reasonable option
- ❌ Already have working algorithm (REFACTOR phase)


TPP Workflow

RED Phase

1. Look at current code state
2. Consider next test options
3. Choose test that uses highest-priority transformation
4. Write that test

GREEN Phase

1. Look at failing test
2. Consider transformation options
3. Choose highest-priority transformation that works
4. Apply transformation to pass test
5. If forced to use low-priority transformation:
   - Question if there's simpler test first
   - Consider backtracking

REFACTOR Phase

TPP doesn't apply here - use refactorings, not transformations

Further Reading


Summary

TPP = A methodology for choosing tests and transformations that avoids TDD impasses

Core concepts:
1. Transformations change behavior (specific → generic)
2. Transformations have priority (simple → complex)
3. Prefer high-priority transformations
4. Choose tests that use high-priority transformations
5. Wrong order leads to impasses

When properly applied:
- Smooth TDD flow
- Algorithms emerge naturally
- Small, safe steps
- No rewrites or impasses

# Supported AI Coding Agents

This skill is compatible with the SKILL.md standard and works with all major AI coding agents:

Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.