| name | diagnose |
| description | 어려운 버그와 성능 회귀(regression)를 위한 체계적 진단 루프. 재현 → 최소화 → 가설 → 계측 → 수정 → 회귀 테스트. 사용자가 "진단해줘" / "디버그해줘"라고 하거나, 버그를 보고하거나, 뭔가 깨졌다/throwing/실패하고 있다고 말하거나, 성능 회귀를 설명할 때 사용. |
Diagnose
어려운 버그를 위한 규율. 명시적으로 정당화될 때만 단계를 건너뛰어.
코드베이스를 탐색할 때, 프로젝트의 도메인 용어집을 사용해서 관련 모듈에 대한 명확한 멘탈 모델을 만들고, 건드리는 영역의 ADR을 확인.
Phase 1 — 피드백 루프 구축
이게 핵심 스킬. 나머지는 다 기계적. 버그에 대한 빠르고 결정적이며 에이전트가 실행 가능한 pass/fail 신호가 있다면, 원인을 찾을 수 있어 — 이분법, 가설 검증, 계측은 모두 그 신호를 소비할 뿐. 신호가 없다면, 코드를 아무리 째려봐도 구원받지 못해.
여기에 비례적으로 큰 노력을 쏟아. 공격적으로. 창의적으로. 포기하기를 거부해.
만드는 방법 — 대략 이 순서로 시도
- 실패 테스트 — 버그에 도달하는 어느 seam에서든 (단위, 통합, e2e).
- Curl / HTTP 스크립트 — 실행 중인 dev 서버에 대해.
- CLI 호출 — 고정 입력으로 stdout을 알려진 정상 스냅샷과 diff.
- Headless 브라우저 스크립트 (Playwright / Puppeteer) — UI를 구동하고 DOM/콘솔/네트워크에 대해 단언.
- 캡처된 트레이스 재생. 실제 네트워크 요청 / payload / 이벤트 로그를 디스크에 저장하고, 격리된 코드 경로로 재생.
- 버려질 하니스(harness). 시스템의 최소 부분만 띄우고 (서비스 1개, mock된 의존성), 단일 함수 호출로 버그 코드 경로를 실행.
- 속성 / fuzz 루프. 버그가 "가끔 잘못된 출력"이라면, 1000개의 무작위 입력을 돌려서 실패 모드를 찾아.
- 이분법 하니스. 버그가 두 알려진 상태(commit, dataset, 버전) 사이에 나타났다면, "상태 X로 부팅, 확인, 반복"을 자동화해서
git bisect run으로 실행 가능하게.
- 차분(differential) 루프. 같은 입력을 구버전 vs 신버전 (또는 두 config)에 통과시키고 출력을 diff.
- HITL bash 스크립트. 마지막 수단. 사람이 클릭해야 한다면,
.agents/skills/diagnose/scripts/hitl-loop.template.sh로 _사람_을 구동해서 루프가 구조화되도록. 캡처된 출력이 너에게 피드백.
올바른 피드백 루프를 만들면, 버그는 90% 수정된 셈.
루프 자체를 반복 개선
루프를 제품처럼 다뤄. 일단 어떤 루프든 만들었으면, 자문해:
- 더 빠르게 만들 수 있나? (셋업 캐싱, 무관 init 스킵, 테스트 범위 축소.)
- 신호를 더 날카롭게? ("crash 안 함"이 아니라 특정 증상에 단언.)
- 더 결정적으로? (시간 고정, RNG 시드, 파일시스템 격리, 네트워크 동결.)
30초짜리 flaky 루프는 루프가 없는 것보다 약간 나을 뿐. 2초짜리 결정적 루프는 디버깅 슈퍼파워.
비결정적 버그
목표는 깔끔한 재현이 아니라 재현률 상승. 트리거를 100× 반복, 병렬화, 스트레스 추가, 타이밍 윈도우 좁히기, sleep 주입. 50% flake 버그는 디버깅 가능. 1%는 안 됨 — 디버깅 가능할 때까지 비율을 올려.
진짜로 루프를 만들 수 없을 때
멈추고 명시적으로 말해. 시도한 것을 나열해. 사용자에게 요청: (a) 재현되는 환경 접근권, (b) 캡처된 아티팩트 (HAR 파일, 로그 덤프, core dump, 타임스탬프 있는 화면 녹화), 또는 (c) 임시 프로덕션 계측 추가 허가. 루프 없이 가설로 진행하지 마.
믿을 수 있는 루프가 생기기 전까지 Phase 2로 진행하지 마.
Phase 2 — 재현
루프 실행. 버그가 나타나는 것을 봐.
확인:
버그를 재현하기 전까지 진행하지 마.
Phase 3 — 가설
검증 전에 3-5개의 순위가 매겨진 가설을 생성. 단일 가설 생성은 첫 번째 그럴듯한 아이디어에 닻을 내림.
각 가설은 반증 가능해야 함: 그것이 만드는 예측을 진술해.
형식: "만약 가 원인이라면, <Y를 바꾸면> 버그가 사라질 것 / <Z를 바꾸면> 더 나빠질 것."
예측을 진술할 수 없다면, 가설은 그냥 vibe — 버리거나 날카롭게.
검증 전에 사용자에게 순위 리스트를 보여줘. 도메인 지식으로 즉시 재순위 매길 수 있음 ("3번에 방금 변경 배포했어"), 또는 이미 배제한 가설을 알 수도 있음. 저렴한 체크포인트, 큰 시간 절약. 사용자가 자리에 없으면 막히지 말고 — 너의 순위로 진행.
Phase 4 — 계측
각 프로브는 Phase 3의 특정 예측에 매핑되어야 함. 한 번에 한 변수만 변경.
도구 선호:
- 디버거 / REPL 검사 — 환경이 지원한다면. 중단점 1개가 로그 10개를 이김.
- 타겟팅된 로그 — 가설을 구별하는 경계에서.
- 절대 "다 로그 찍고 grep" 하지 마.
모든 디버그 로그에 태그, 예: [DEBUG-a4f2]. 끝에 정리할 때 grep 한 번으로 끝남. 태그 없는 로그는 살아남음. 태그 있는 로그는 죽음.
성능 분기. 성능 회귀의 경우, 로그는 보통 틀림. 대신: 베이스라인 측정 확립 (타이밍 하니스, performance.now(), 프로파일러, 쿼리 플랜), 그 다음 이분법. 측정 먼저, 수정 나중.
Phase 5 — 수정 + 회귀 테스트
회귀 테스트를 수정 전에 작성 — 단, 올바른 seam이 있을 때만.
올바른 seam이란 테스트가 호출 지점에서 발생하는 실제 버그 패턴을 실행하는 곳. 사용 가능한 seam이 너무 얕다면 (버그가 여러 호출자를 필요로 하는데 단일 호출자 테스트, 버그를 트리거한 체인을 복제할 수 없는 단위 테스트), 그곳의 회귀 테스트는 거짓 자신감을 줌.
올바른 seam이 없다면, 그 자체가 발견. 기록해. 코드베이스 아키텍처가 버그를 잡아두는 것을 막고 있는 것. 다음 단계를 위해 표시.
올바른 seam이 있다면:
- 최소화된 재현을 그 seam에서 실패 테스트로 변환.
- 실패하는 것을 봐.
- 수정 적용.
- 통과하는 것을 봐.
- 원래(최소화 안 된) 시나리오에 대해 Phase 1 피드백 루프 재실행.
Phase 6 — 정리 + 사후 분석
완료 선언 전 필수:
그 다음 자문: 무엇이 이 버그를 예방했을까? 답이 아키텍처 변경 (좋은 테스트 seam 부재, 얽힌 호출자, 숨은 결합)을 포함한다면 구체적인 내용과 함께 /improve-codebase-architecture 스킬로 넘겨. 권장은 수정이 들어간 후에 — 시작할 때보다 더 많은 정보가 있어.