---
name: refactor-with-tests
description: 'Safely refactor code with test-first methodology — capture behavior, refactor, verify nothing broke.'
argument-hint: 'Target module/file and refactor goal, e.g. "backend/src/modules/auth — extract token service", "simplify scheduler engine in FlowMonk"'
agent: agent
---

# Refactor with Tests Prompt

Safely refactor code by first capturing existing behavior in tests, then restructuring while keeping all tests green.

## Context — ByteLyst Conventions

- **Test runner:** Vitest (TypeScript), pytest (Python)
- **Test location:** Same module directory or `__tests__/`
- **Never modify tests to make them pass** — fix the code
- **Commit format:** `refactor(scope): description`

## Refactor Protocol

### Step 1: Understand Current Behavior

Before touching any code:

1. **Read the code** — understand what it does, not just the structure
2. **Read existing tests** — understand what's already covered
3. **Run existing tests** — confirm they pass:
   ```bash
   pnpm test
   pnpm typecheck
   ```
4. **Identify the API surface** — what do consumers depend on?
   - Exported functions/types
   - REST endpoint contracts
   - Side effects (database writes, events emitted)

### Step 2: Add Missing Tests (Before Refactoring)

If test coverage is insufficient, add tests for current behavior FIRST:

```typescript
// Capture existing behavior before refactoring
describe('<Module> — pre-refactor behavior capture', () => {
  it('should return X when given Y', () => {
    // Test current behavior, even if the implementation is ugly
  });

  it('should throw NotFoundError for missing items', () => {
    // Edge cases that must be preserved
  });

  it('should include productId in all created documents', () => {
    // Ecosystem invariants
  });
});
```

**Commit these tests separately:**
```bash
git add .
git commit -m "test(<scope>): capture pre-refactor behavior (N tests)"
```

### Step 3: Plan the Refactor

Document what will change and what must stay the same:

- **Changing:** Internal structure, file organization, naming
- **Preserving:** API surface, behavior, test contracts
- **New patterns:** What you're moving toward (e.g., extract service class, split module)

### Step 4: Refactor in Small Steps

For each step:

1. Make ONE structural change
2. Run tests: `pnpm test`
3. Run typecheck: `pnpm typecheck`
4. If tests pass → commit
5. If tests fail → undo and retry with smaller step

```bash
# After each small refactor step
pnpm test && pnpm typecheck && \
git add . && git commit -m "refactor(<scope>): <what changed>"
```

### Step 5: Verify Completeness

After all refactoring:

```bash
# Full validation
pnpm build && pnpm typecheck && pnpm test

# Check no exports were broken
# If this is a @bytelyst/* package:
cd learning_ai_common_plat && pnpm build && pnpm test
```

### Step 6: Update Documentation

If the refactor changed:
- File locations → update AGENTS.md repo layout
- API surface → update README.md or API docs
- Module structure → update any roadmap or architecture docs

```bash
git add .
git commit -m "docs(<scope>): update docs after <module> refactor"
git push
```

## Common Refactor Patterns

### Extract Service Class
```
Before: routes.ts has business logic mixed with HTTP handling
After:  routes.ts → service.ts → repository.ts (routes are thin)
```

### Split Module
```
Before: modules/schedule/ has 500-line routes.ts
After:  modules/schedule/ + modules/schedule-entries/ (each focused)
```

### Extract Shared Package
```
Before: Two products have duplicate utility code
After:  New @bytelyst/* package consumed by both
```

### Consolidate Re-exports
```
Before: Each file imports from @bytelyst/* directly
After:  src/lib/ re-exports, internal files import from src/lib/
```

## Guardrails

- **Test first, refactor second.** Never refactor without test coverage.
- **Small commits.** Each commit should be revertible without losing other changes.
- **No behavior changes.** Refactoring changes structure, not behavior. If you're fixing bugs, that's a separate commit.
- **No export changes** without updating all consumers.
- **Run the full suite** after every commit, not just the changed module's tests.
