d3ro-voice/.claude/skills/refactor-wave/SKILL.md
2026-07-23 11:14:13 +09:00

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. 검증 체크리스트

워크스트림 통합 후 반드시:

  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로 재검증):

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 추가 시 본 표 갱신.