Targeted Refactor
This walkthrough shows a module refactor using the targeted-refactor tactic. The tactic uses two variables to scope the refactor precisely: target_module identifies which code to refactor, and refactor_goal defines what the refactor should achieve. Both variables are substituted into stage prompts so every agent works with the same scope and objective.
The scenario
Your project's authentication module (src/auth/) has grown complex over time. It started as a single file but now has tangled dependencies between token generation, session management, and password hashing. You want to simplify its internal structure without changing the public API that the rest of the codebase depends on.
You have the targeted-refactor tactic in your project:
# .lineup/tactics/targeted-refactor.yaml
name: targeted-refactor
description: |
Refactor a specific module. Researches the module's current structure and
dependencies, plans the refactor, implements it, and verifies no regressions.
variables:
- name: target_module
description: "Path to the module to refactor (e.g., src/auth/, lib/db.ts)"
default: "src/"
- name: refactor_goal
description: "What should the refactor achieve?"
default: "Improve readability and reduce complexity"
stages:
- type: research
agent: researcher
prompt: |
Analyze ${target_module} thoroughly: file structure, public API surface,
internal dependencies, external consumers, and test coverage. Identify
code smells, complexity hotspots, and coupling issues. Map which files
import from this module so we understand the blast radius.
- type: plan
agent: architect
prompt: |
Plan the refactor of ${target_module} to ${refactor_goal}. Prioritize
changes that reduce complexity without changing the public API. Define
a safe execution order that keeps tests passing at each step.
- type: implement
agent: developer
- type: verify
agent: reviewer
prompt: |
Verify the refactor of ${target_module} did not break anything. Run the
full test suite. Check that the public API surface is unchanged. Confirm
the refactor goal was met: ${refactor_goal}.
verification:
- "Public API of the module is unchanged"
- "All existing tests pass"
- "No new circular dependencies introduced"
- "Code complexity is reduced (fewer lines, simpler control flow)"Starting the pipeline
You type:
/lineup:kick-off targeted-refactorThe orchestrator finds the tactic and sees it defines two variables. Before executing any stages, it prompts you for each one:
Variable Input Required
This tactic requires input before starting.
target_module: Path to the module to refactor (e.g., src/auth/, lib/db.ts) [default: src/]
You type:
User Input
src/auth/
The orchestrator prompts for the second variable:
Variable Input Required
refactor_goal: What should the refactor achieve? [default: Improve readability and reduce complexity]
You type:
User Input
Separate token, session, and password concerns into independent files with no cross-dependencies
The orchestrator substitutes both values into every stage prompt that references them and begins.
Stage 1/4: Research
The researcher agent receives a prompt with ${target_module} replaced by src/auth/. It analyzes the module's structure, public API, dependencies, and blast radius.
Stage 1/4 -- Research
Analyzing src/auth/ thoroughly...
Research complete. Key findings:
- File structure:
src/auth/index.ts-- re-exports public API (login, logout, register, refreshToken, hashPassword, verifyPassword)src/auth/auth.ts-- 340 lines, contains all logic in one filesrc/auth/types.ts-- TypeScript interfaces for tokens and sessions
- Public API surface: 6 functions exported from
index.ts - Internal issues:
auth.tshas 3 concerns mixed: token generation (JWT), session management (Redis), and password hashing (bcrypt)- Token functions call session functions directly and vice versa, creating circular internal dependencies
- hashPassword and verifyPassword are pure functions but import the Redis client because they share a file with session logic
- Cyclomatic complexity of
auth.ts: 24 (high)
- External consumers: 8 files import from
src/auth/src/routes/login.ts,src/routes/register.ts,src/routes/logout.tssrc/routes/refresh.ts,src/middleware/auth.tssrc/routes/__tests__/login.test.ts,register.test.ts,auth.test.ts
- Test coverage: 78% on
auth.ts, but tests are fragile because mocking Redis for password tests (password functions don't use Redis) - No circular dependencies at the module level, but internal coupling makes the file hard to modify safely
The researcher mapped the blast radius: 8 files import from src/auth/, all through the index.ts barrel export.
Stage 2/4: Plan
The architect agent receives the research findings. Its prompt includes both substituted variables: plan the refactor of src/auth/ to Separate token, session, and password concerns into independent files with no cross-dependencies.
Stage 2/4 -- Plan
Here is the proposed refactor plan:
Execution order (tests pass after each step):
Extract password functions
- Create
src/auth/password.tswith hashPassword, verifyPassword - These are pure functions with only a bcrypt dependency
- Remove them from
auth.ts, add import inauth.tstemporarily - Update
index.tsto re-export frompassword.ts - Run tests -- should pass (public API unchanged)
- Create
Extract token functions
- Create
src/auth/token.tswith generateToken, refreshToken, verifyToken - Dependencies: JWT library only
- Remove from
auth.ts, update internal references - Update
index.tsto re-export fromtoken.ts - Run tests -- should pass
- Create
Extract session functions
- Create
src/auth/session.tswith createSession, destroySession, getSession - Dependencies: Redis client
auth.tsnow only contains login/logout/register orchestration- Update
index.tsto re-export fromsession.ts - Run tests -- should pass
- Create
Simplify
auth.tsto orchestration only- Rename to
src/auth/auth.ts(keep name, reduce to ~60 lines) - login, logout, register compose token + session + password
- No direct Redis or bcrypt imports -- only imports from sibling files
- Run tests -- should pass
- Rename to
Remove unnecessary test mocks
- Password tests no longer need Redis mocks
- Token tests no longer need Redis mocks
- Update test files to remove stale mock setup
Approval Gate
Do you approve this plan? (yes/no/suggest changes)
The plan defines a safe execution order where tests pass after each step, and it preserves the public API by keeping index.ts as the barrel export. You type yes.
Stage 3/4: Implement
The developer agent follows the plan step by step:
Stage 3/4 -- Implement
Implementing the approved plan...
- Step 1: Extract password functions
- Created
src/auth/password.ts- hashPassword and verifyPassword moved from
auth.ts - Only dependency: bcrypt
- hashPassword and verifyPassword moved from
- Updated
src/auth/index.ts- Re-exports hashPassword, verifyPassword from
password.ts
- Re-exports hashPassword, verifyPassword from
- Created
- Step 2: Extract token functions
- Created
src/auth/token.ts- generateToken, refreshToken, verifyToken moved from
auth.ts - Only dependency: jsonwebtoken
- generateToken, refreshToken, verifyToken moved from
- Updated
src/auth/index.ts- Re-exports token functions from
token.ts
- Re-exports token functions from
- Created
- Step 3: Extract session functions
- Created
src/auth/session.ts- createSession, destroySession, getSession moved from
auth.ts - Only dependency: Redis client
- createSession, destroySession, getSession moved from
- Updated
src/auth/index.ts- Re-exports session functions from
session.ts
- Re-exports session functions from
- Created
- Step 4: Simplify
auth.ts- Updated
src/auth/auth.ts- Reduced from 340 lines to 58 lines
- login, logout, register now compose from sibling imports
- No direct bcrypt, JWT, or Redis imports
- Updated
- Step 5: Clean up tests
- Updated
src/routes/__tests__/auth.test.ts- Removed unnecessary Redis mock from password test cases
- Updated
src/routes/__tests__/login.test.ts- Removed stale Redis mock setup
- Updated
Implementation complete. 3 files created, 4 files modified.
Stage 4/4: Verify
The reviewer agent receives a prompt with both variables substituted. It checks that src/auth/ was not broken and that the refactor goal was met.
Stage 4/4 -- Verify
Verification report:
- Build: PASS
- Tests: 31 run, 31 passed, 0 failed
- Verification criteria:
- Public API of the module is unchanged: PASS (
index.tsexports the same 6 functions, all 8 consumer files unchanged) - All existing tests pass: PASS (31 tests, 0 failures, 0 skipped)
- No new circular dependencies introduced: PASS (
password.ts,token.ts,session.tshave no cross-imports;auth.tsimports from siblings only) - Code complexity is reduced: PASS (
auth.ts: 340 lines -> 58 lines, cyclomatic complexity: 24 -> 4; largest new file issession.tsat 85 lines with complexity 6)
- Public API of the module is unchanged: PASS (
- Status: PASS
Final result
| Stage | Agent | What happened |
|---|---|---|
| Research | researcher | Mapped module structure, found 3 tangled concerns in one 340-line file |
| Plan | architect | 5-step execution order, tests pass after each step |
| Implement | developer | 3 files created, 4 files modified, auth.ts reduced to 58 lines |
| Verify | reviewer | All 31 tests pass, public API unchanged, no circular dependencies |
The refactored module is in your working directory, ready for review. The public API surface is identical -- all 8 consumer files work without changes.
Key patterns in this walkthrough
Two variables for precise scoping. The target_module and refactor_goal variables let you specify exactly what to refactor and why. Without variables, you would need to describe the scope and objective in a long task description and hope each agent interprets it correctly.
Variable substitution in prompts. Both ${target_module} and ${refactor_goal} appear in the research, plan, and verify stage prompts. The researcher analyzes src/auth/ specifically. The architect plans toward the stated goal. The reviewer checks that the goal was met. Every agent works from the same substituted values.
Safe execution order. The plan defines five steps where tests pass after each one. This means if any step breaks something, the failure is isolated to that step. The developer follows this order, and the reviewer can verify that the incremental approach was respected.
Public API preservation. The tactic's verification criteria explicitly check that the module's public API is unchanged. The index.ts barrel export acts as a contract: as long as it exports the same functions with the same signatures, consumer files are unaffected. The reviewer confirms all 8 importing files work without modification.