--- name: doubt-driven-development description: 사소하지 않은 결정이 성립하기 전에, 매번 새 컨텍스트의 적대적 리뷰를 거치게 합니다. 진행하기 전에 모든 가정을 캐묻고 싶을 때, 계획에 숨은 실패 모드가 있는지 스트레스 테스트할 때, 속도보다 정확성이 중요할 때, 익숙하지 않은 코드에서 작업할 때, 이해관계가 클 때(프로덕션 인증, 보안에 민감한 로직, 고위험 마이그레이션, 되돌릴 수 없는 작업), 또는 자신 있는 출력을 나중에 디버깅하는 것보다 지금 검증하는 편이 더 쌀 때 사용하세요. --- # Doubt-Driven Development ## 개요 자신 있는 답이 곧 옳은 답은 아닙니다. 긴 세션에서는 컨텍스트가 쌓이면서, 아무도 알아차리지 못한 채 가정이 조용히 "사실"로 바뀝니다. Doubt-driven development는 사소하지 않은 출력이 성립하기 전에, 승인 쪽이 아니라 **반증** 쪽으로 기운 새 컨텍스트 리뷰어를 구체화하는 규율입니다. 이것은 `/review`가 아닙니다. `/review`는 완성된 아티팩트에 대한 판정입니다. 이것은 진행 중인 자세입니다. 방향 수정이 아직 쌀 때, 사소하지 않은 결정을 캐묻습니다. ## 언제 사용하나 다음 중 하나라도 해당하면 그 결정은 **사소하지 않습니다**. - 분기 로직을 새로 만들거나 수정한다 - 모듈이나 서비스 경계를 넘는다 - 타입 시스템이나 컴파일러가 검증할 수 없는 속성을 단언한다(스레드 안전, 멱등성, 순서, 불변 조건) - 올바름이 미래의 독자가 볼 수 없는 컨텍스트에 의존한다 - 영향 범위가 되돌릴 수 없다(프로덕션 배포, 데이터 마이그레이션, 공개 API 변경) 다음의 경우 이 스킬을 적용하세요. - 불확실한 상황에서 아키텍처 결정을 내리려 할 때 - 사소하지 않은 코드를 커밋하려 할 때 - 자명하지 않은 사실을 주장하려 할 때("이건 안전하다", "이건 확장된다", "이건 스펙과 일치한다") - 완전히 이해하지 못한 코드에서 작업할 때 **사용하지 말아야 할 때:** - 기계적인 작업(이름 변경, 포매팅, 파일 이동) - 명확하고 모호하지 않은 사용자 지시를 따를 때 - 기존 코드를 읽거나 요약할 때 - 올바름이 명백한 한 줄 변경 - 순수한 도구 작업(테스트 실행, 파일 나열) - 사용자가 검증보다 속도를 명시적으로 요청한 경우 키 입력마다 의심하면 아무것도 내놓지 못합니다. 이 스킬은 위에서 정의한 사소하지 않은 결정에만 적용합니다. ## 로딩 제약 이 스킬은 **메인 세션 오케스트레이터**용으로 설계되었습니다. 여기서 3단계(아래의 DOUBT)가 새 컨텍스트 리뷰어를 띄울 수 있습니다. - **페르소나의 `skills:` frontmatter에 이 스킬을 넣지 마세요.** 3단계를 따르는 페르소나는 또 다른 페르소나를 띄우게 됩니다. 이는 `../../references/orchestration-patterns.md`가 명시적으로 금지하는 오케스트레이션 안티패턴입니다("personas do not invoke other personas", 페르소나는 다른 페르소나를 호출하지 않는다). - **서브에이전트 컨텍스트 안에서 이 스킬을 적용하고 있다면**(Claude Code가 중첩 서브에이전트 생성을 막는 경우): 바람직한 경로는 사용자에게 doubt-driven을 중첩해서 실행할 수 없다고 알리고, 메인 세션이 처리하게 하는 것입니다. 최후의 수단으로만, 저하된 자기 질문 폴백이 있습니다. ARTIFACT와 CONTRACT를 이전 추론과 단단히 갈라 놓은 새로운 자기 프롬프트로 다시 쓰고, 1-5단계를 밟으세요. 이것은 **새 컨텍스트 리뷰가 아닙니다**(자신의 컨텍스트를 그대로 들고 갑니다). 결과를 저하된 것으로 표시하고, 사용자에게 닿을 수 있을 때는 언제나 에스컬레이션을 우선하세요. ## 절차 이 스킬을 적용할 때 이 체크리스트를 복사하세요. ``` 의심 사이클: - [ ] 1단계: CLAIM. 주장과, 그것이 중요한 이유를 적었다 - [ ] 2단계: EXTRACT. 아티팩트와 계약을 분리하고, 추론은 걷어냈다 - [ ] 3단계: DOUBT. 적대적 프롬프트로 새 컨텍스트 리뷰어를 호출했다 - [ ] 4단계: RECONCILE. 모든 발견 사항을 아티팩트 본문에 대조해 분류했다 - [ ] 5단계: STOP. 중단 조건을 충족했다(사소한 발견, 3사이클, 또는 사용자 재정의) ``` ### 1단계: CLAIM. 성립시키려는 것을 드러내기 결정을 두세 줄로 이름 붙이세요. ``` CLAIM: "스펙에 적힌 읽기 중심 워크로드에서 새 캐싱 계층은 스레드 안전하다." WHY THIS MATTERS: 여기서 경쟁 상태가 나면 사용자 데이터가 깨지고, QA에서 발견하기 어렵다. ``` 그렇게 짧게 주장을 쓸 수 없다면, 결정이 아니라 감입니다. 꼼꼼히 따지기 전에 먼저 드러내세요. ### 2단계: EXTRACT. 리뷰 가능한 최소 단위 새 컨텍스트 리뷰어에게 필요한 것은 여정이 아니라 **아티팩트**와 **계약**입니다. - 코드: diff나 함수. 파일 전체가 아닙니다 - 결정: 3-5문장의 제안과, 그것이 만족해야 하는 제약 조건 - 단언: 주장과, 그것을 뒷받침한다고 여겨지는 근거(1단계 CLAIM 블록과는 구분해서 둡니다. CLAIM 블록은 검증 대상인 오케스트레이터의 가설입니다) 추론은 걷어내세요. 결론을 넘기면, 그 결론을 추인하는 답이 돌아옵니다. 단위는 리뷰어가 한 번 읽고 머리에 담을 수 있을 만큼 작아야 합니다. 500줄짜리 풀 리퀘스트라면 먼저 분해하세요. 걷어낼 것은 추론입니다. 근거는 남기세요. 호출자, 가드, 테스트가 주장과 모순되면 그 단위에 포함하세요. ### 3단계: DOUBT. 새 컨텍스트 리뷰어 호출 리뷰어의 프롬프트는 **반드시 적대적**이어야 합니다. 프레이밍이 답을 결정합니다. ``` 적대적 리뷰. 이 아티팩트의 잘못된 점을 찾으세요. 작성자가 과신한다고 가정하세요. 다음을 살펴보세요. - 명시되지 않은 가정 - 처리되지 않은 엣지 케이스 - 숨은 결합이나 공유 상태 - 계약이 깨질 수 있는 방식 - 이것이 깨뜨릴 수 있는 기존 관례 - 예상 밖 입력에서의 실패 모드 타당성을 확인하지 마세요. 요약하지 마세요. 문제를 찾거나, 철저히 살펴본 뒤에도 찾지 못했다면 그 사실을 명시하세요. ARTIFACT: <아티팩트를 붙여 넣기> CONTRACT: <계약을 붙여 넣기> ``` **ARTIFACT와 CONTRACT만 넘기세요. CLAIM은 넘기지 마세요.** 리뷰어에게 결론을 건네면 동조 쪽으로 기울어집니다. 리뷰어는 아티팩트가 계약을 만족하는지 독립적으로 판단해야 합니다. Claude Code에서 `agents/`의 역할 기반 리뷰어는 설계상 격리된 컨텍스트로 시작하므로 여기서 쓸 수 있습니다. 명단과 도메인별 대응은 `agents/`를 보세요. **위의 적대적 프롬프트는 페르소나의 기본 응답 형태보다 우선합니다.** `code-reviewer` 같은 페르소나는 강점과 약점을 함께 담은 균형 잡힌 판정을 내도록 작성되어 있습니다. doubt-driven에는 문제만 담은 출력이 필요합니다. 호출 시 적대적 프롬프트를 그대로 붙여 넣어 페르소나의 기본값을 덮어쓰세요. 페르소나의 응답 형태를 깔끔하게 덮어쓸 수 없으면, 적대적 프롬프트를 붙인 일반 서브에이전트로 돌아가세요. #### 교차 모델 에스컬레이션 단일 모델 리뷰어는 원래 작성자와 맹점을 공유합니다. 더 차갑고 구조가 다른 모델이 그 맹점을 잡습니다. Doubt-driven은 이미 사소하지 않은 결정에만 선택적으로 적용되므로, 그 범위 안에서 교차 모델을 제안하는 일은 스킬이 주는 가치의 일부입니다. 빼도 되는 마찰이 아닙니다. **대화형 세션에서는 항상 제안하세요. 조용히 건너뛰지 마세요.** **1단계: 사용자에게 묻기** 위의 3단계 단일 모델 리뷰가 끝난 뒤, RECONCILE 전에 멈추고 물어보세요. > *"단일 모델 리뷰가 끝났습니다. 교차 모델의 2차 의견을 원하시나요? 선택지: Gemini CLI, Codex CLI, 수동 외부 리뷰(다른 곳에 붙여 넣기), 또는 건너뛰기."* 이 질문은 모든 대화형 의심 사이클에서 필수입니다. 이해관계가 낮아 보이는 아티팩트라도 마찬가지입니다. 그 비용을 치를 가치가 있는지는 에이전트가 아니라 사용자가 결정합니다. 에이전트의 일은 선택지를 드러내는 것입니다. **2단계: 사용자가 CLI를 고르면, 확인한 다음 호출** 1. 도구가 PATH에 있는지 확인하세요(`which gemini`, `which codex`). 2. 전체 프롬프트를 넘기기 전에 동작하는지 테스트하세요(`gemini --version` 또는 동등한 명령). 오래되었거나 깨진 바이너리는 `which`는 통과해도 실제 입력에서 실패할 수 있습니다. 3. 필요한 플래그, 인증, 환경 변수(예: API 키)를 포함해 정확한 호출을 사용자와 확인하세요. 구현마다 다릅니다. 추측하지 마세요. 4. ARTIFACT + CONTRACT + 적대적 프롬프트**만** 넘기세요. 세션 컨텍스트도, CLAIM도 넘기지 마세요. 5. 셸 이스케이프를 신경 쓰세요. 아티팩트에 따옴표, `$(...)`, 백틱이 있으면 인라인 `-p "…"`보다 표준 입력(`echo … | gemini`)이나 heredoc을 선호하세요. 확신이 없으면 실행 전에 사용자에게 호출을 확인해 달라고 하세요. 6. 출력을 4단계(RECONCILE)로 가져가세요. **아티팩트를 셸 따옴표로 감싼 인자 안에 끼워 넣지 마세요.** 코드, 마크다운, 리뷰 프롬프트에는 백틱, `$(...)`, 따옴표 문자가 흔히 들어 있고, 그러면 프롬프트가 잘리거나 안에 들어 있는 셸이 실행됩니다. 전체 프롬프트를 파일에 쓰고 표준 입력으로 파이프하세요. 예시 형태(설치된 도구에 맞춰 플래그를 확인하세요. 구현과 버전에 따라 문법이 다릅니다): ```bash # Write the adversarial prompt + ARTIFACT + CONTRACT to a temp file first. # Then pipe via stdin so shell metacharacters in the artifact stay inert. # Codex (read-only sandbox keeps the CLI from writing to your workspace): codex exec --sandbox read-only -C - < /tmp/doubt-prompt.md # Gemini ('--approval-mode plan' is read-only; '-p ""' triggers non-interactive # mode and the prompt is read from stdin): gemini --approval-mode plan -p "" < /tmp/doubt-prompt.md ``` 읽기 전용 샌드박스가 핵심입니다. 의심 아티팩트 자체에 지시가 들어 있을 수 있습니다. 의도적이든 우연이든 프롬프트 인젝션입니다. 읽기 전용이 아니면 교차 모델 CLI가 그 지시를 워크스페이스에 대해 실행할 수 있습니다. **3단계: CLI를 쓸 수 없거나 실패하면** 실패를 명시적으로 드러내세요. 수동으로 실행하기, 다른 도구를 시도하기, 건너뛰기를 제안하세요. 단일 모델로 조용히 돌아가지 마세요. 사용자는 교차 모델이 실행되지 않았음을 알아야 합니다. **4단계: 사용자가 건너뛰면** 출력에서 건너뛰었음을 인정하세요(*"단일 모델 발견 사항만으로 진행합니다"*). 그리고 RECONCILE로 계속하세요. 건너뛰는 것은 괜찮습니다. 조용히 건너뛰는 것은 안 됩니다. **비대화형 컨텍스트**(CI, `/loop`, autonomous-loop, 예약 실행): - 교차 모델은 **건너뛰며**, 그 사실을 출력에 **알려야** 합니다. *"교차 모델 건너뜀: 비대화형 컨텍스트."* - **사용자의 명시적 허가 없이 외부 CLI를 호출하지 마세요.** 이것은 핵심 안전 속성입니다. 교차 모델은 비용, 지연 시간, 도구의 취약함을 더합니다. 에이전트는 매 사이클 선택지를 드러내고, 이 아티팩트에 그것이 필요한지는 사용자가 결정합니다. ### 4단계: RECONCILE. 발견 사항을 다시 접기 리뷰어의 출력은 판정이 아니라 데이터입니다. **오케스트레이터는 여전히 당신입니다.** 분류하기 전에 각 발견 사항을 아티팩트 본문과 다시 대조해 읽으세요. 리뷰어에게 그대로 도장을 찍는 것은 리뷰어를 무시하는 것과 같은 실패입니다. 각 발견 사항을 이 **우선순위**로 분류하세요. 먼저 맞는 분류가 이깁니다. 1. **계약 오독.** 리뷰어가 집어낸 이유가 구체적으로, 제공한 CONTRACT가 불명확하거나 불완전했기 때문입니다. 먼저 계약을 고치고, 다음 사이클에서 다시 분류하세요. 2. **유효하고 조치 가능.** 아티팩트를 바꿔야 하는 실제 문제입니다. 바꾸고 다시 루프하세요. 3. **유효한 트레이드오프.** 문제는 실제지만, 고치는 비용이 받아들이는 비용보다 큽니다. 사용자가 보도록 트레이드오프를 명시적으로 기록하세요. 4. **잡음.** 리뷰어가 갖지 못한 컨텍스트에서는 실제로 올바른 것을 집어낸 경우입니다. 기록하고 넘어가세요. 그리고 물어보세요. 그 컨텍스트를 계약에 넣었다면 잘못된 지적을 막을 수 있었는가? 새 리뷰어는 컨텍스트가 없어서 틀릴 수 있습니다. "새롭다"는 이유만으로 판단을 넘기지 마세요. ### 5단계: STOP. 재귀가 아닌, 한계가 있는 루프 다음일 때 멈추세요. - 다음 반복이 사소하거나 이미 고려한 발견만 돌려줄 때, **또는** - 3사이클을 완료했을 때(사용자에게 에스컬레이션하세요. 네 번째를 혼자 밀어붙이지 마세요), **또는** - 사용자가 명시적으로 "ship it"이라고 말할 때 3사이클 후에도 리뷰어가 실질적인 문제를 계속 드러내면, 아티팩트가 준비되지 않은 것일 수 있습니다. 이것을 사용자에게 드러내세요. 해결되지 않은 세 사이클은 루프를 계속할 이유가 아니라, 아티팩트에 대한 정보입니다. 아티팩트가 커서 3사이클이 "명백히 부족"하다면, 아티팩트가 너무 큰 것입니다. 2단계로 돌아가 분해하세요. 상한을 올리지 마세요. ## 흔한 합리화 | 합리화 | 현실 | |---|---| | "자신 있으니 의심 단계를 건너뛰자" | 새로운 문제에서 확신은 정확성과 잘 맞지 않습니다. 확실하다고 느끼는 순간이야말로 맹점이 숨는 때입니다. | | "리뷰어를 띄우는 것은 비싸다" | 프로덕션에서 잘못된 커밋을 디버깅하는 편이 더 비쌉니다. 검사에는 한계가 있습니다. 버그에는 없습니다. | | "리뷰어는 트집만 잡는다" | 범위가 없을 때만 그렇습니다. 프롬프트를 "이 계약 아래에서 이것을 실패하게 만들 문제"로 제한하세요. | | "마지막에 `/review`로 의심하면 된다" | `/review`는 최종 게이트입니다. Doubt-driven은 방향 수정이 쌀 때 잘못된 방향을 일찍 잡습니다. 풀 리퀘스트 시점이면 너무 늦습니다. | | "단계마다 의심하면 영원히 내놓지 못한다" | 이 스킬은 모든 키 입력이 아니라 사소하지 않은 결정에 적용됩니다. "사용하지 말아야 할 때"를 다시 읽으세요. | | "의견이 둘이면 항상 하나보다 낫다" | 두 번째 의견의 컨텍스트가 더 적고 잡음만 낼 때는 그렇지 않습니다. 대조해서 분류하세요. 판단을 넘기지 마세요. | | "리뷰어가 반대했으니 내가 틀렸다" | 리뷰어에게는 당신의 컨텍스트가 없습니다. 불일치는 판정이 아니라 정보입니다. 아티팩트를 다시 읽고, 분류한 다음, 결정하세요. | | "교차 모델이 항상 더 낫다" | 교차 모델은 단일 모델이 자기 자신과 공유하는 맹점을 잡습니다. 동시에 비용과 도구의 취약함을 더합니다. 대화형 의심 사이클마다 제안하세요. 이 아티팩트에 그것이 필요한지는 사용자가 결정합니다. 에이전트의 일은 선택지를 드러내는 것이지, 그것을 통과시킬지 막는 것이 아닙니다. | | "사용자가 한 번 동의했으니 CLI를 계속 호출해도 된다" | 호출마다 허가는 따로입니다. 아티팩트, 프롬프트, 플래그는 호출 사이에 바뀝니다. 매 실행 전에 정확한 명령을 사용자와 다시 확인하세요. | ## 레드 플래그 - 한 줄 이름 변경이나 포매팅 변경에 새 컨텍스트 리뷰어를 띄우는 것 - 아티팩트 본문을 다시 읽지 않고 리뷰어 출력을 권위 있는 것으로 취급하는 것 - 사용자에게 에스컬레이션하지 않고 3사이클을 넘겨 도는 것 - 리뷰어에게 "문제를 찾으세요" 대신 "이게 좋은가요?"로 묻는 것 - 이해관계가 큰 결정인데 시간 압박을 이유로 의심을 건너뛰는 것 - 바뀌지 않은 아티팩트에 새 컨텍스트를 다시 띄우는 것(같은 발견이 나옵니다. 시간을 끄는 것입니다) - **의심 연극(확인 가능한 신호)**: 리뷰어가 실질적인 발견을 드러낸 사이클이 2회 이상인데, 조치 가능으로 분류된 발견이 하나도 없습니다. 의심하는 것이 아니라 타당성만 확인하고 있습니다. 멈추고 에스컬레이션하세요. - 커밋한 뒤에야 의심하는 것. 그것은 `/review`이고, doubt-driven development가 아닙니다 - 도구가 존재하고, 설정되어 있으며, 그 정확한 문법을 받아들이는지 사용자와 확인하지 않은 채 외부 CLI 호출을 하드코딩하는 것 - **대화형 의심 사이클에서 교차 모델을 조용히 건너뛰는 것.** 권하지 않을 때조차 제안은 보여야 합니다. 건너뛰는 것은 괜찮습니다. 조용히 건너뛰는 것은 안 됩니다. - 외부 CLI가 오류를 내거나 없을 때 조용히 돌아가는 것. 실패를 드러내고 사용자가 방향을 바꾸게 하세요 - 리뷰어 입력에서 계약을 빼는 것 - 리뷰어에게 CLAIM을 넘기는 것(동조 쪽으로 기울어집니다) ## 다른 스킬과의 관계 - **`code-review-and-quality` / `/review`**: 보완 관계입니다. `/review`는 사후 풀 리퀘스트 판정이고, doubt-driven은 진행 중인 결정별 검증입니다. 둘 다 사용하세요. - **`source-driven-development`**: SDD는 *프레임워크에 대한 사실*을 공식 문서와 대조해 검증합니다. Doubt-driven은 *아티팩트에 대한 당신의 추론*을 검증합니다. SDD는 API가 존재하는지 확인하고, doubt-driven은 계약 아래에서 그것을 올바르게 썼는지 확인합니다. - **`test-driven-development`**: TDD의 RED 단계는 구체화된 의심입니다. 실패하는 테스트는 반증 시도입니다. TDD가 적용될 때, 그 실패하는 테스트*가* 동작 주장에 대한 의심 단계입니다. - **`debugging-and-error-recovery`**: 리뷰어가 실제 실패 모드를 드러내면, 디버깅 스킬로 들어가 위치를 좁히고 고치세요. - **저장소 오케스트레이션 규칙**(`../../references/orchestration-patterns.md`): 이 스킬은 메인 세션에서 오케스트레이션합니다. 페르소나가 다른 페르소나를 호출하는 것은 안티패턴 B입니다. 위의 로딩 제약을 보세요. ## 검증 doubt-driven development를 적용한 뒤: - [ ] 사소하지 않은 결정(위의 정의에 따라)은 성립하기 전에 CLAIM으로 명시적으로 이름 붙였다 - [ ] 사소하지 않은 아티팩트마다 새 컨텍스트 리뷰가 최소 한 번 있었다(동작 주장에서는, 다른 스킬과의 관계에 적힌 대로 TDD의 RED 단계가 만든 실패하는 테스트가 이를 만족합니다) - [ ] 리뷰어는 ARTIFACT + CONTRACT를 받았다. CLAIM은 받지 않았고, 당신의 추론도 받지 않았다 - [ ] 리뷰어 프롬프트는 적대적이었다("문제를 찾으세요"). 타당성을 확인하는 질문("이게 좋은가요?")이 아니었다 - [ ] 발견 사항을 아티팩트 본문에 대조해 분류했다(그대로 도장 찍지 않았다). 우선순위는 계약 오독 / 조치 가능 / 트레이드오프 / 잡음이다 - [ ] 중단 조건을 충족했다(사소한 발견, 3사이클, 또는 사용자 재정의) - [ ] 대화형 모드에서 교차 모델을 사용자에게 **명시적으로 제안**했다(아티팩트의 이해관계와 무관하게). 그리고 그 응답을 출력에서 인정했다 - [ ] 비대화형 모드에서 교차 모델을 건너뛰었고, 그 사실을 알렸다 - [ ] 외부 CLI 호출 전에 PATH 확인, 동작하는 바이너리 테스트, 사용자와의 문법 확인, 실행에 대한 명시적 허가가 있었다