--- name: refactor-wave description: 대규모 리팩토링·코드 정리·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 등장. ```bash git reset HEAD # 모든 staging 해제 git add # 정밀하게 add git commit -m "..." # 커밋 ``` 워크스트림당 1 커밋 원칙. `git add -A` / `git add .` 금지. ### R6. 에이전트는 커밋하지 않는다 - 에이전트 프롬프트에 "**커밋하지 마세요**" 명시 - 메인 작업자(=Claude 본체)가 통합 검토 + 분리 커밋 - 에이전트는 "변경 파일 목록 + 요약 + git status" 보고만 ### R7. 검증 체크리스트 워크스트림 통합 후 반드시: 1. `npm run typecheck` GREEN (packages 변경 시 모노레포 전체). 단 codebase에 pre-existing 타입에러가 있으면 "내가 건드린 영역/workspace GREEN"으로 재해석 + pre-existing은 별도 wave. 2. `npm run lint` — 전체가 pre-existing 부채로 붕괴 시(P9), **변경 파일만 lint해 errors 0**를 게이트로. `git diff --name-only HEAD | grep -E '\.(ts|tsx)$' | xargs npx eslint` (삭제 파일 제외). 3. `npm run test` GREEN 4. `npm run build` GREEN (빌드 영향 워크스트림만이라도) 5. UI 변경 있으면 `npm run dev`로 실제 화면 1회 확인 (WS3는 화면별 즉시) 6. **배포(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로 재검증): ```bash 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 수행). 필터링 없이 바로 구현에 들어가면 "이미 되어있는 걸 또 하거나", "잘못된 전제로 리팩토링"할 위험. 1. HIGH 보고 각 발견 → 착수 전 파일 Read/Grep 1회 (30초) 2. 실제 상태와 다르면 → KEEP+사유로 분류 전환 3. 보고서 별 섹션에 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 추가 시 본 표 갱신.