15 KiB
| name | description |
|---|---|
| refactor-wave | 대규모 리팩토링·코드 정리·SSOT 확립 워크플로우 하네스. 사용자가 "리팩토링 한번 싹", "정리 좀 하고 가자", "공격적 감사", "워크스트림 발사", "토큰 일원화", "데드코드 정리", "SSOT", "/simplify 제대로", "더 정리할 여지" 등을 언급하면 반드시 사용. 정책 합의 → 공격적 감사 → 멀티 에이전트 병렬 워크스트림 → 분리 커밋 → 보고서 순서를 강제한다. |
리팩토링 Wave 워크플로우 하네스 (D3RO-VOICE)
트리거
/refactor-wave슬래시 명령- "리팩토링 싹", "정리 좀", "공격적 감사", "워크스트림", "SSOT", "데드코드", "토큰 일원화", "더 정리할 여지" 키워드
- /simplify 후 사용자가 "더 큰 정리"를 요청할 때
- Phase 완료 후 누적 부채 청소가 필요한 시점
절대 규칙 (Non-Negotiable)
R1. 정책을 먼저 박는다
감사·작업 전에 docs/REFACTOR_POLICY.md 합의·갱신. 미합의 정책 위에 작업하면 1주 후 회귀 + 1달 후 갈팡질팡.
- 8개 결정 포인트 표준: 디자인 토큰(d3roPalette/theme.ts) / IPC 채널·타입 SSOT / i18n(t() + locale 키) / 서비스 경계(싱글톤+EventEmitter) / 상태 관리(RecognitionState+AudioState) / packages↔apps 의존 방향 / DRY 임계치 / 자동화(lint·typecheck·훅)
- 새 결정 포인트 발견 시 정책 문서 즉시 추가
- 결정 근거(외부 리서치 포함)는 정책 문서 별도 섹션에 박기
R2. 공격적 감사 모드 강제
보수적 에이전트는 5건 보고하고 끝남. 공격적 모드 명시:
- "false negative > false positive"
- "건들지 말자" 결론 금지
- "DELETE / REPLACE / REFACTOR / KEEP+사유" 4가지 중 명시
- "20~40개 발견되어야 정상"
- 확신도(HIGH/MED/LOW) 명시해서 사용자 필터링 가능
R3. 멀티 에이전트 병렬 발사 — 파일 충돌 사전 분리
워크스트림은 파일 영역이 겹치지 않도록 분할:
| WS | 영역 |
|---|---|
| WS1 | 토큰/SSOT 정의 (packages/ui/src/theme.ts, theme-vars.ts, components/ds/) |
| WS2 | 메인 프로세스 (apps/desktop/src/main/services/, preload, shared 타입, IPC) |
| WS3 | 렌더러 UI (apps/desktop/src/renderer/, 화면별 분담) — WS1 의존 |
| WS4 | i18n (packages/i18n/src/locales/, t() 키 정리) |
| WS5 | 빌드/설정/팝업 (package.json, electron.vite.config, electron-builder.yml, turbo.json, Vanilla JS 팝업) |
- 같은 파일을 두 워크스트림이 건드리면 충돌 → 분담 시 영역 명시
- WS3는 WS1 산출물(토큰 정의) 의존이라 1차 완료 후 발사
- 화면별 분담(Dashboard/History/Settings/Meeting/Chat 등)으로 추가 병렬화 가능
- V2 모노레포 주의:
packages/core·ui·i18n은 여러 앱이 공유 — packages 변경 시 영향 앱(desktop/web/mobile) 전부 typecheck
R4. 에이전트 작업 위치 명시
메인 작업 디렉터리 vs worktree 혼동 차단:
- 에이전트 프롬프트에 "메인 디렉터리
D:\workspace\D3ROVoice" 절대경로 명시 - "shell cwd가 worktree로 잡히면 절대경로로 접근" 강조
- 에이전트가 변경한 파일이 메인에 있는지 worktree에 있는지 보고서에서 확인
R5. 커밋 직전 staging 청소
처음 분리 커밋 시 가장 흔한 실수: 이전 staging 묻어들어가 한 커밋에 19 files 등장.
git reset HEAD # 모든 staging 해제
git add <ws-specific-files> # 정밀하게 add
git commit -m "..." # 커밋
워크스트림당 1 커밋 원칙. git add -A / git add . 금지.
R6. 에이전트는 커밋하지 않는다
- 에이전트 프롬프트에 "커밋하지 마세요" 명시
- 메인 작업자(=Claude 본체)가 통합 검토 + 분리 커밋
- 에이전트는 "변경 파일 목록 + 요약 + git status" 보고만
R7. 검증 체크리스트
워크스트림 통합 후 반드시:
npm run typecheckGREEN (packages 변경 시 모노레포 전체). 단 codebase에 pre-existing 타입에러가 있으면 "내가 건드린 영역/workspace GREEN"으로 재해석 + pre-existing은 별도 wave.npm run lint— 전체가 pre-existing 부채로 붕괴 시(P9), 변경 파일만 lint해 errors 0를 게이트로.git diff --name-only HEAD | grep -E '\.(ts|tsx)$' | xargs npx eslint(삭제 파일 제외).npm run testGREENnpm run buildGREEN (빌드 영향 워크스트림만이라도)- UI 변경 있으면
npm run dev로 실제 화면 1회 확인 (WS3는 화면별 즉시) - 배포(tag push) 전 런타임 게이트 — v0.2.1-alpha 교훈, 강제: typecheck/lint/test GREEN만으로 런타임을 보장하지 않는다. tag push 전 반드시
npm run dev로 main 프로세스 기동을 확인 + 가능하면 산물 설치 e2e. 특히 config 기본값 누락 / 타입 캐스팅(as never) 제거 / 의존성 제거 / 인터페이스-구현체 분리 변경은 런타임 크래시에 직결 — 정적 검증(GREEN)에 속아 동작 확인을 건너뛰지 말 것. (사례: WS2가 AppConfig 인터페이스에 키 추가 +as never제거했으나 CONFIG_DEFAULTS 기본값 누락 → typecheck GREEN but 런타임undefined.map()크래시 → v0.2.0-alpha 배포 실패, 사용자가 발견.)
R8. 보고서 + 하네스 동시 작성
1차 완료 시 두 문서 필수:
docs/REFACTOR_WAVE{N}_REPORT.md— 정량 산출물 + SKIP 사유 + 후속 계획 + 메타 학습- 본 SKILL.md 갱신 — 이번 wave에서 배운 패턴 반영
표준 절차 (8단계)
Step 1. 진입점 결정
- 트리거 발동 → 사용자 의도 확인
- 범위 합의 (desktop만? packages/web/mobile/site 포함?)
- 실행 검증 빈도 합의 (화면별 vs 한꺼번에)
Step 2. 정책 작성·갱신
docs/REFACTOR_POLICY.md작성 또는 갱신- 8개 결정 포인트 사용자에게 한 줄씩 OK 받기
- 모르겠다는 항목은 비교 옵션 + 앱 특성 기반 추천 + 외부 리서치까지 제공
- 외부 의존성(Electron, Ollama, faster-whisper 등) 결정은 WebSearch/context7로 최신 정보 확인 후
Step 3. 공격적 감사 (3 에이전트 병렬)
Explore 에이전트 3개에 다음 영역 분담:
- Agent A: 메인 프로세스 — 서비스 패턴 위반(싱글톤/EventEmitter), IPC 채널·타입 불일치, D3ROError 미사용, 데드코드, TODO/stub
- Agent B: 렌더러/UI — hex 하드코딩, d3roPalette/d3roTypo/d3roShadow 미사용, DS 컴포넌트 미활용, i18n 하드코딩(t() 미사용), any/console.log
- Agent C: 리소스/설정 — 미사용 의존성, locale 키 누락·고아 키, 문서-코드 정합(CLAUDE.md 서비스 목록 vs 실제), 스크립트/빌드 설정
각 에이전트 프롬프트에 "공격적 모드 4원칙"(R2) 박기. 결과 통합 시 중복 제거 + 우선순위 표 작성.
유용한 감사 grep (근사 필터, 결과는 반드시 Read로 재검증):
grep -rnE ':\s*any\b' apps/desktop/src packages --include="*.ts" --include="*.tsx"
grep -rn 'console\.log' apps/desktop/src packages --include="*.ts" --include="*.tsx"
grep -rnE '#[0-9a-fA-F]{3,8}\b' apps/desktop/src/renderer packages/ui/src --include="*.tsx" | grep -v theme
grep -rnE '>[가-힣]+' apps/desktop/src/renderer --include="*.tsx" # 하드코딩 한국어 후보
Step 4. 우선순위 합의
3 에이전트 결과를 다음 표로 정리해 사용자에게 제시:
- 🟢 빠르고 안전 (각 5~10분)
- 🟡 중간 작업 (각 30분~1시간)
- 🔴 대규모 (반나절~하루, 위험도 있음)
- ⚠️ 사용자 판단 필요 (의도 확인)
번들로 묶어서 "번들 N: ... 시간 예상" 식으로 추천.
Step 5. 워크스트림 분리 + 의존성 그래프
- 파일 충돌 없도록 영역 분할 (R3)
- 의존성 그래프 작성 (예: WS1 → WS3)
- 1차/2차/3차 wave로 시간순 분류
Step 6. 멀티 에이전트 병렬 발사
- 1차 wave: 의존성 없는 워크스트림 모두 병렬 (보통 3~4개)
- 각 에이전트 프롬프트에 R4(작업 위치), R6(커밋 금지), R7(검증) 박기
- P8: 에이전트에게 "본인 영역 typecheck를 직접 실행해 EXIT 0 확인; 안 되면 원인 보고"를 hard requirement로 명시 (회피 방지)
- 결과 받으면 영역별 변경 파일 매핑 → 충돌 여부 확인. 에이전트가 typecheck를 안 돌렸으면 통합 단계에서 반드시 재검증
Step 7. 통합 + 분리 커밋
git reset HEAD로 staging 청소 (R5)- 워크스트림당 1 커밋
- 커밋 메시지 형식:
type(scope): 한 줄 요약 (WS{N})+ 본문에 변경 카테고리 + SKIP 사유 + 정책 참조 - Co-Authored-By, Claude 관련 문구 절대 금지
- 사용자 별도 작업(다른 도구가 만든 파일)은 별도 커밋 또는 보존
Step 8. 보고서 + 다음 wave 계획
docs/REFACTOR_WAVE{N}_REPORT.md작성 (R8)- 본 SKILL.md 갱신 (배운 패턴 추가)
memory/project_status.md갱신- 사용자에게 다음 wave 진행 여부 결정 요청
안티패턴 (Don't)
A1. 정책 없이 패치 시작
"일단 보이는 것부터 고치자" → 1주 후 동일 영역 다시 건드림. R1 위반.
A2. 단일 에이전트로 큰 감사
보수적 에이전트는 5건만 보고. 공격적 모드 + 3 에이전트 병렬이 표준.
A3. 같은 파일 두 워크스트림 동시 수정
git 충돌 + 의도 분리 불가. R3 위반.
A4. 에이전트가 직접 커밋
커밋 메시지 일관성 깨짐 + 통합 검토 생략 → 회귀 위험. R6 위반.
A5. git add -A 또는 git add .로 분리 커밋 시도
이전 staging이 묻어들어가서 의도와 다른 파일들이 한 커밋에 합쳐짐. 정밀 add 필수. R5 위반.
A6. SKIP 사유 모호하게 적기
"복잡해서 SKIP"은 1달 후 재시도 시 같은 함정. SKIP은 "기술적 제약 + 별도 Phase 필요" 명확히.
A7. 보고서 없이 끝내기
다음 wave 시작할 때 1차 산출물 추적 불가. R8 위반.
검증 체크리스트 (각 wave 완료 시)
- 정책 문서 갱신 완료 (
docs/REFACTOR_POLICY.md) - 워크스트림별 분리 커밋 (각 1 커밋)
typecheck+lint+test모두 GREEN- UI 변경 시
npm run dev실행 확인 완료 (또는 미해당 명시) - SKIP 항목 사유 + 후속 Phase 명시
docs/REFACTOR_WAVE{N}_REPORT.md작성- 본 SKILL.md 갱신 (배운 패턴 추가)
memory/project_status.md업데이트- 누적 커밋 사용자에게 보고
검증된 범용 인사이트 (HaramLog wave 1~10에서 이식)
P1. 감사 에이전트 HIGH 확신도 보고도 착수 전 30초 재검증 필수
공격적 모드 에이전트는 false positive를 기꺼이 보고한다 (정책대로 R2 수행). 필터링 없이 바로 구현에 들어가면 "이미 되어있는 걸 또 하거나", "잘못된 전제로 리팩토링"할 위험.
- HIGH 보고 각 발견 → 착수 전 파일 Read/Grep 1회 (30초)
- 실제 상태와 다르면 → KEEP+사유로 분류 전환
- 보고서 별 섹션에 false positive 기록 → 다음 wave 학습 데이터
P2. "순수 이동" 분할에도 동작 변경이 숨는다
거대 파일(god component/service)을 쪼갤 때 import가 빠지면 에이전트가 동작을 바꿔서 컴파일을 통과시키는 경우가 있다 (원 사례: modifier 체인을 no-op으로 대체해 import 누락 회피 → UI 회귀). 통합 후 반드시:
- "순수 이동"일수록 diff가 1:1이어야 함 —
git show HEAD:<원본>과 분할 결과의 핵심 로직(훅 체인, useEffect deps, 이벤트 구독/해제) 대조 - 단순화된 흔적(제거된 wrapper, 사라진 cleanup)은 곧 회귀 의심
P3. 거대 파일 분할은 "순수 이동"이 90%
거대 컴포넌트/서비스는 이미 작은 함수들의 모음이라 클러스터별 파일 분리만으로 해소된다. 진짜 재설계(상태 끌어올리기, 서비스 분리)는 분리 후에도 남는 명백한 압력(props 20+개)이 있을 때만. 사용자가 "재설계 포함"을 골라도 먼저 순수 이동을 시도하고, 재설계는 분리 결과를 보고 판단.
P4. 화면 1개에 고유한 수치는 토큰화하지 말고 file-private 상수로 격리
WAVE_BAR_COUNT = 9처럼 의미가 화면 1개에 고유한 값은 d3ro 토큰 부적합. 명명상수 + 위치 응집(파일 상단)으로 충분. 토큰 변환 강제하지 말 것.
P5. 비컴포넌트 함수의 한국어 반환은 i18n 키 반환으로 전환
서비스/유틸 함수가 한국어 리터럴을 반환하면 다국어 불가. 함수는 i18n 키(또는 enum)를 반환하고, 렌더링 시점에 t()로 풀이. 메인 프로세스 문자열(팝업/트레이 메뉴)도 동일 패턴.
P6. 이름 충돌은 역할 접미어로 예방
패키지가 달라도 이름이 같으면 import alias 강제 + 독자 혼란. 도메인 타입과 UI 토큰이 겹치면 UI 쪽에 역할 접미어 (예: RecordingState enum vs RecordingStateColor 토큰 맵).
P7. 공유 패키지 토큰 변경은 전체 소비 앱에 영향 — "부분 치환 + 별칭 제거" 불가
packages/ui의 토큰(deprecated 별칭 등)을 제거하려면, 그 토큰을 import하는 모든 앱(desktop/web/admin/site/mobile/ui-native)을 먼저 치환해야 함. 한 앱만 치환하고 공유 패키지에서 별칭을 지우면 다른 앱이 타입에러+런타임 undefined로 깨짐. (Wave 1 사례: accent.amber — desktop 치환 후에도 web/admin/site 사용으로 별칭 유지, 전체 치환 별도 wave로 이월.) DP6(전체 typecheck 강제)이 이걸 방어.
P8. 감사/구현 에이전트는 typecheck를 회피한다 — 프롬프트에 "EXIT 0 직접 확인"을 hard requirement로
에이전트가 "메인 작업자가 통합 후 수행 권장" / "환경 이슈로 로컬 실행 필요" 핑계로 typecheck를 안 돌리고 보고하는 경우가 잦음. 에이전트 프롬프트에 "typecheck는 본인이 직접 실행해 EXIT 0를 확인; 안 되면 원인을 명확히 보고(무한 재시도 금지)"를 명시. 통합 단계에서도 반드시 재검증.
P9. lint 게이트의 pre-existing 부채 — "내 영역 errors 0"으로 재해석
codebase가 원래 lint GREEN이 아닌 경우가 많음(루트 eslint .가 다른 앱의 config 문제로 붕괴, pre-existing errors 다수). "통합 후 lint GREEN"(R7 원문)이 비현실적. 게이트는 "내가 건드린 영역의 lint errors 0" + "pre-existing은 별도 wave로 분리"로 재해석. (검증 팁: git diff --name-only HEAD로 변경 파일만 lint → 내 책임 분리.)
누적 wave 인덱스
| Wave | 일시 | 산출 커밋 | 보고서 |
|---|---|---|---|
| Wave 1 | 2026-07-22 | b820c78(WS1)·aef4428(WS2)·078304d(WS3)·87f2dac(WS4) | docs/REFACTOR_WAVE1_REPORT.md |
| Wave 2 | 2026-07-22 | efac690(AMBER)·660e622(NATIVE)·00a99e4(POPUP)·4c25620(DEPS)·5b6e7aa(PATTERN) | docs/REFACTOR_WAVE2_REPORT.md |
다음 wave 추가 시 본 표 갱신.