Claude Code의 잘못 설계된 자동 진행 기능 해부

3 days ago 13
  • Claude Code 2.1.198은 AskUserQuestion에 60초간 답이 없으면 모델이 자체 판단으로 작업을 계속하는 자동 진행 기능을 기본 활성화했지만, 출시 당시 변경 로그와 문서에는 기록하지 않았음
  • 권한 요청을 자동 승인하지는 않았으나, 이미 허용된 도구나 --dangerously-skip-permissions 환경에서는 “staging 또는 production” 같은 의사결정 게이트를 대신 통과할 수 있었고 일부 답변만 입력해도 나머지는 모델이 선택했음
  • 문제 제기 약 이틀 뒤 나온 2.1.200은 기능을 삭제하지 않고 기본값만 껐으며, /config에서 60s, 5m, 10m, never 중 하나를 고르는 옵트인 방식으로 전환함
  • 공개 저장소에는 실제 제품 소스나 도입·되돌림 커밋이 없지만, npm의 Bun 실행 파일에 포함된 JavaScript 번들을 비교해 2.1.198에서 AFK 문자열·스키마·분석 이벤트가 함께 추가됐음을 확인할 수 있음
  • 기본 자동 업데이트와 불완전한 변경 기록이 결합하면 사용자 개입 없이 안전 가정이 달라질 수 있음. CLI를 고정하면서 플러그인은 갱신하려면 DISABLE_AUTOUPDATER=1과 FORCE_AUTOUPDATE_PLUGINS=1 을 함께 설정해야 함

60초 뒤 사람 대신 결정한 2.1.198

  • 2026년 7월 1일 출시된 Claude Code 2.1.198은 AskUserQuestion이 사람의 답을 기다린 지 60초가 지나면 차단을 해제하고, 모델에 문맥을 바탕으로 최선의 판단을 내려 계속하라고 지시했음
    • 출력에는 No response after 60s — continued without an answer가 표시됨
    • 다시 질문할 수 있다는 안내도 있었지만 재질문에도 같은 타임아웃이 적용됨
  • 사용자가 일부 질문에만 답한 상태에서 자리를 비우면 입력을 폐기하지 않고 부분 답변을 제출했음
    • 세 질문 중 첫 번째에만 답하면 해당 답과 모델이 고른 나머지 답으로 작업을 계속함
    • 화면 메시지는 답변 여부에 따라 continued with the answers selected so far 또는 continued without an answer를 선택함
  • 화면에 카운트다운이 전혀 없었던 것은 아님
    • 키를 누르면 타이머가 다시 시작됐고 auto-continue in 12s · any key to stay 같은 문구가 나타남
    • 다만 CLAUDE_AFK_COUNTDOWN_MS 기본값이 20초여서 처음 40초 동안은 일반적인 차단 질문처럼 보였고, 경고는 마지막 20초에만 표시됨
    • 여러 에이전트를 각기 다른 탭에서 실행하거나 자리를 비운 사용자는 경고를 보지 못할 수 있음

적용 범위와 안전 게이트의 한계

  • 타임아웃은 AskUserQuestion에만 적용됐으며, 계획 승인이나 Do you want to allow … 같은 권한 프롬프트에는 연결되지 않았음
    • 실행 파일에서 카운트다운 구성 요소와 타이머 훅은 질문 대화상자에만 연결됨
    • 도구 레퍼런스도 권한 프롬프트가 유휴 상태에서 자동 해결되지 않는다고 명시함
  • 권한 프롬프트가 애초에 나타나지 않는 실행 방식에서는 이러한 분리가 보호 장치가 되지 못함
    • bypassPermissions, acceptEdits, allowedTools, --dangerously-skip-permissions, PreToolUse 훅을 이용할 수 있었음
    • 배포 명령이 허용 목록에 있거나 권한 확인을 우회했다면 AskUserQuestion의 “staging 또는 production?”, “어떤 config인가?” 같은 선택이 남은 유일한 게이트일 수 있음
  • 타이머가 권한을 부여하지는 않았지만, 이미 권한이 있는 작업의 선택 자체를 모델에 맡길 수 있었음
  • 도구 스키마에는 timeout 입력이 없고 모델이 이를 설정하거나 제어할 수도 없었음
    • 입력 매개변수는 questions, answers, annotations, metadata뿐임
    • 답을 건너뛴 주체는 모델이 아니라 응답을 자동 반환한 에이전트 하네스였음

출시와 되돌림 연표

  • 확인된 공개 기록은 다음 순서로 이어짐
    • 2026-06-29: 2.1.196 출시, 신고자가 추정한 마지막 정상 버전
    • 2026-06-30: 2.1.197 출시, 변경 로그에는 Sonnet 5 출시 한 줄만 존재
    • 2026-07-01: 자동 진행이 들어간 2.1.198 출시
    • 2026-07-02 02:54 UTC: Aleksey Nogin이 이슈 #73125를 등록
    • 2026-07-02 03:45 UTC: 댓글에서 비공개 탈출구였던 CLAUDE_AFK_TIMEOUT_MS를 공유
    • 2026-07-02: 이슈가 열린 상태에서 24개 항목을 담은 2.1.199 출시, 자동 진행은 여전히 미기재
    • 2026-07-03: 2.1.200에서 기본 동작을 되돌림
    • 2026-07-04 18:04 UTC: 이슈 종료
  • 이슈에는 384개 👍와 143개 댓글이 달렸으며, 신고 환경은 2.1.198, Opus, AWS Bedrock, VS Code 터미널이었음
  • 문제 신고부터 기본값 전환까지 약 이틀이 걸림

2.1.200이 수정한 방식

  • 2.1.200은 기능을 제거하지 않고 자동 진행을 기본 비활성화했으며 /config를 통한 옵트인으로 바꿈
  • 수정 전 2.1.198에는 /config 설정인 askUserQuestionTimeout이 없었고, 영향을 피하려면 출시 문서에 나오지 않은 환경 변수를 사용해야 했음
  • 2.1.211에서도 전체 구현은 유지됨
    • /config 항목 이름은 Question auto-continue timeout
    • 허용값은 60s, 5m, 10m, never
    • 설정하지 않으면 never로 처리돼 기능이 꺼짐
    • 내부 기본 타임아웃은 여전히 60,000ms, 카운트다운 기준은 20,000ms
    • CLAUDE_AFK_TIMEOUT_MS와 CLAUDE_AFK_COUNTDOWN_MS가 설정값을 덮어쓸 수 있음
  • 수정은 타이머를 삭제한 것이 아니라, 설정값 또는 환경 변수가 있을 때만 활성화하도록 게이트 조건 하나를 추가한 것
  • 영향을 받는 버전에 고정된 사용자는 settings.json에서 CLAUDE_AFK_TIMEOUT_MS에 매우 큰 값을 넣어 임시로 우회할 수 있었음

변경 로그에서 빠진 기능 도입

  • 릴리스 노트는 공식 변경 로그와 저장소의 CHANGELOG.md에 같은 내용으로 게시됨
  • 당시 기록을 고정 커밋 기준으로 보면 자동 진행의 추가는 어느 버전에도 나타나지 않음
    • 2.1.197: Sonnet 5 출시 한 줄
    • 2.1.198: 약 30개 항목이 있지만 자동 진행은 없음
    • 2.1.199: 이슈가 열린 뒤 게시된 24개 항목에도 없음
  • AskUserQuestion은 2.0.55 이후 13개 버전에서 총 15회 등장할 만큼 평소 변경 로그에 기록되던 도구였음
    • 하지만 변경이 들어간 구간인 2.1.181부터 2.1.200 전까지는 한 번도 나타나지 않음
    • 켜짐과 꺼짐이라는 두 번의 동작 변경 가운데 꺼짐만 기록됨
  • 자동 진행을 처음 기록한 유일한 항목은 2.1.200의 “기본적으로 더 이상 자동 진행하지 않으며 /config로 옵트인”한다는 문구였음
  • CLAUDE_AFK_TIMEOUT_MS는 변경 로그와 README 어디에도 없었음

동작을 되돌린 뒤 추가된 문서

  • 현재 환경 변수 레퍼런스는 두 AFK 환경 변수를 기록하고, 2.1.198과 2.1.199에서 60초 자동 진행이 기본 활성화됐다고 명시함
  • 이 문서는 출시 당일 상태를 담은 문서일 수 없음
    • 현재 문구가 안내하는 askUserQuestionTimeout은 2.1.198 실행 파일에 존재하지 않음
    • Wayback Machine의 6월 23일 및 7월 1일 12:11·21:35 UTC 캡처에는 CLAUDE_AFK와 AskUserQuestion이 각각 0회였음
    • 대조 항목인 DISABLE_AUTOUPDATER는 모든 캡처에서 2회 확인돼 페이지 수집 실패로 인한 누락이 아니었음
  • 2.1.198은 7월 1일 16:50:16 UTC에 npm에 게시됐지만, 4시간 45분 뒤의 문서에도 AFK, 자동 진행, 질문 도구, 카운트다운이 없었음
  • 관련 문구는 7월 1일 21:35부터 7월 5일 13:58 사이에 추가됐고, 이 구간에는 2.1.200의 되돌림도 포함됨
  • 새 문서는 이미 “기본 비활성화”된 수정 후 상태만 기록해, 7월 1~2일의 실제 기본 동작이 문서화된 시점은 없었음

공개 저장소에 없는 제품 소스와 커밋

  • 기능을 도입한 공개 커밋과 되돌린 공개 커밋은 모두 없음
  • 공개 Git 기록에는 변경 로그와 feed.xml만 갱신한 자동 커밋 두 개가 남음
    • 75709ea: 2.1.198 릴리스 노트 게시
    • 1322e9b: 2.1.200 릴리스 노트 게시
  • anthropics/claude-code 저장소에는 216개 추적 파일이 있고 그중 104개가 Markdown이며, 실제 배포 제품 소스는 없음
    • 실행 가능한 파일은 예제나 이슈 추적 자동화 스크립트임
    • plugins/와 examples/도 샘플 플러그인, GCP 게이트웨이 Terraform 설정, MDM 프로필 등을 담음
  • 2.1.197과 2.1.198 태그의 차이는 CHANGELOG.md와 이를 RSS로 반복한 feed.xml뿐임
    • 2.1.196부터 2.1.206까지 열 번의 연속 릴리스도 같은 두 파일만 바뀜
    • 태그는 소스 릴리스가 아니라 사실상 릴리스 노트 태그
  • 변경 로그나 저장소·태그 비교로는 실제 배포 동작을 확인할 수 없으며, 작성된 소스는 공개되지 않은 채 컴파일된 실행 파일로만 배포됨

의도적으로 설계하고 계측한 흔적

  • 공식 설계 문서, PR, 변경 로그 문구 등 도입 이유를 확인할 자료는 없음
  • 이름과 메시지는 자리를 비운 사람 때문에 병렬 에이전트가 무기한 차단되는 상황을 겨냥했음을 보여주는 정황 증거
    • 내부 이름 AFK는 away from keyboard를 뜻함
    • 메시지는 “사용자가 키보드에서 떨어져 있을 수 있다”고 가정함
    • 이슈 참가자들은 수십 개 에이전트가 며칠 동안 사람의 답을 기다리는 작업 흐름을 사용한다고 밝혔음
  • 2.1.198에는 자동 해결 여부를 나타내는 afkTimeoutMs 필드가 도구 결과 스키마에 추가됨
    • 사람이 답한 경로에는 없고 자동 해결 시 유휴 시간을 기록함
    • 모델에 자동 해결된 응답임을 알리고 터미널 출력 구성 요소를 선택하는 데 쓰임
  • 같은 버전에 tengu_ask_user_question_afk_auto_advance 분석 이벤트도 추가됨
    • timeoutMs, 질문 수, 계획 모드 여부, 부분 답변 여부를 전송함
    • 질문 원문은 보내지 않고 source_hash와 카운터를 사용함
  • hadPartialAnswers가 별도 계측된 만큼 부분 답변 경로도 독립된 코드와 측정을 갖고 있었음
  • 동작, 카운트다운 UI, 스키마 필드, 분석 이벤트가 한 버전에 함께 들어갔으므로 우발적인 한 줄이 아니라 측정 체계를 갖춘 기능이었음
  • 이 증거만으로 누가 작성·검토·승인·병합했는지, 사람이 어느 단계에 개입했는지는 알 수 없음

닫힌 소스지만 읽을 수 있는 Bun 실행 파일

  • 설치된 Claude Code는 약 250MB의 심볼이 제거되지 않은 네이티브 실행 파일
  • Bun 단일 실행 파일 형식은 런타임 뒤에 모듈 그래프를 붙이며, Claude Code 실행 파일에서도 ---- Bun! ---- 표식을 찾을 수 있음
  • Bun이 JavaScript 번들을 실행 파일 안에 포함하므로 strings로 설정 이름, 메시지, 분석 이벤트 등을 읽을 수 있음
  • 로컬 설치 디렉터리는 최근 버전 일부만 보관하지만, npm에는 각 버전의 플랫폼별 실행 파일이 남아 있음
    • @anthropic-ai/claude-code는 약 152KB의 설치용 스텁임
    • 실제 Linux x64용 2.1.198 패키지는 약 249MB
  • 공개 소스는 없지만 각 릴리스 아티팩트를 내려받아 배포된 동작을 확인할 수 있으므로 완전한 블랙박스는 아님

2.1.197과 2.1.198 실행 파일 비교

  • 두 실행 파일에서 AFK 관련 문자열을 검색하면 경계가 명확함
    • 2.1.197: away from keyboard, CLAUDE_AFK_TIMEOUT_MS, CLAUDE_AFK_COUNTDOWN_MS가 모두 0회
    • 2.1.198: 각각 2회, 3회, 3회 등장
  • 수정 후 2.1.211의 타이머 게이트에는 /config 값이나 환경 변수가 존재해야 한다는 조건이 추가됨
    • 2.1.198에도 외부 경쟁 실행 여부 등을 검사하는 게이트는 있었음
    • 다만 사용자가 끌 수 있는 조건이 없었고, 수정은 여기에 && 조건 하나를 더한 형태였음
  • 기능명과 문제가 발생한 버전을 모른 채 일반적인 릴리스를 비교하기는 훨씬 어려움
    • strings -n 8 기준 단순 비교에서는 21,903개 문자열이 달라짐
    • 최소 문자열 길이에 따라 81,289개 또는 29,910개로 달라질 수 있어 21,903은 고정된 릴리스 특성이 아님
    • 빌드마다 미니파이어가 식별자 이름을 바꿔 대부분이 실제 기능 변경이 아닌 잡음임
  • 추가된 문자열 중 대문자로 시작하고 일반 문장 형태이며 5단어 이상인 영어 문장만 걸러내면 156줄로 줄어듦
    • 그 안에 Before going idle the user had selected:가 포함됨
    • 대화상자가 사람 대신 답할 때 대화에 삽입하는 문자열이므로, 사전 정보 없이 읽어도 검토를 멈출 만한 변화였음
  • curl, strings, diff로 약 5분 안에 발견할 수 있었지만, 사용자가 모든 자동 업데이트 릴리스마다 실행 파일을 검사하는 방식은 변경 로그를 대신할 수 없음

비용보다 큰 안전 문제

  • 잘못된 경로를 자동 선택하면 불필요한 토큰을 소비할 수 있음
  • 더 큰 문제는 AskUserQuestion을 차단형 안전 게이트로 전제한 훅과 규칙이 60초 카운트다운으로 바뀐다는 점임
  • 배포, 인프라, 프로덕션 인접 스크립트처럼 위험한 환경에서도 Claude Code가 사용됨
  • Claude Code가 기본 자동 업데이트되므로, 조용한 동작 변경과 결합하면 사용자가 아무 조치도 하지 않아도 기존 안전 가정이 달라질 수 있음
  • 최신 버전을 즉시 받는 정책 자체가 위험한 선택일 수 있다는 논의는 On Cooldowns and Dependabot Tuning과 이어짐

자동 업데이트를 끄는 방법과 우선순위

  • 업데이트 비활성화 여부는 다음 환경 변수를 순서대로 검사하며 첫 번째 일치값이 적용됨
    • DISABLE_UPDATES=1: 수동 claude update까지 포함해 모든 업데이트 경로를 차단
    • DISABLE_AUTOUPDATER=1: 백그라운드 확인만 중단하고 수동 업데이트는 허용하며 autoUpdates 설정보다 우선
    • CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1: 자동 업데이트, 피드백 명령, 오류 보고, 원격 측정을 함께 중단
  • 셸 프로필보다 ~/.claude/settings.json의 env 블록에 넣으면 CI, cron, systemd, IDE 터미널을 포함한 모든 세션에 적용할 수 있음
{ "env": { "DISABLE_AUTOUPDATER": "1" } }
  • 설정 범위는 셸 프로필, 사용자별 ~/.claude/settings.json, 저장소별 .claude/settings.json, 중앙 관리용 managed-settings.json 순으로 강해짐
  • managed-settings.json 경로는 다음과 같음
    • macOS: /Library/Application Support/ClaudeCode/managed-settings.json
    • Linux/WSL: /etc/claude-code/managed-settings.json
    • Windows: C:\Program Files\ClaudeCode\managed-settings.json
    • 기존 C:\ProgramData\ClaudeCode 경로는 더 이상 읽지 않음
  • 자동 업데이트를 끄는 CLI 플래그는 없으며 /doctor는 설치 유형과 업데이트 채널을 표시함

CLI 업데이트를 끄면 플러그인도 멈춤

  • 환경 변수나 autoUpdates: false 등 어떤 방식으로든 자동 업데이터를 끄면 플러그인 자동 업데이트도 중단
  • 플러그인 탐색 문서는 DISABLE_AUTOUPDATER와 함께 FORCE_AUTOUPDATE_PLUGINS=1을 설정하면 CLI는 고정하고 플러그인은 계속 갱신할 수 있다고 안내함
  • 반면 실제 자동 업데이트 비활성화를 안내하는 /setup 문서와 설정 환경 변수 표에는 플러그인 영향과 FORCE_AUTOUPDATE_PLUGINS가 없음
  • 실행 파일상 플러그인 업데이트를 멈추는 경로는 네 가지임
    • DISABLE_UPDATES
    • DISABLE_AUTOUPDATER
    • CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC
    • autoUpdates: false
  • 실행 중에는 플러그인이 멈췄다는 경고 대신 디버그 로그 Plugin autoupdate: skipped (auto-updater disabled)만 남음
  • CLI를 고정하고 플러그인을 유지하는 설정은 다음과 같음
{ "env": { "DISABLE_AUTOUPDATER": "1", "FORCE_AUTOUPDATE_PLUGINS": "1" } }
  • FORCE_AUTOUPDATE_PLUGINS, DISABLE_AUTOUPDATER, DISABLE_UPDATES는 1, true, yes, on을 대소문자 구분 없이 참으로 해석하고 0은 거짓으로 처리함
  • CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC는 값이 아니라 존재 여부만 검사해 =0도 활성화하며, 이 경우 자동 업데이터와 플러그인 업데이트가 모두 멈춤

업데이트 설정의 추가 함정

  • 개인정보 보호나 외부 통신 제한 목적으로 CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC를 사용해도 CLI와 플러그인이 함께 고정됨
  • 문서에 없는 autoUpdates: false는 네이티브 설치에서 autoUpdatesProtectedForNative가 활성화되면 무시될 수 있음
    • 환경 변수는 이 설정보다 먼저 검사되고 조건 없이 적용됨
  • 2.1.98은 npm 설치에서 DISABLE_AUTOUPDATER가 레지스트리 버전 확인과 심볼릭 링크 수정을 완전히 막지 못하던 문제를 수정함
  • 자동 업데이터가 ~/.local/bin/claude의 사용자 지정 실행기나 심볼릭 링크를 릴리스마다 덮어쓰던 문제도 수정됐으며, 현재 /doctor는 외부 관리 실행기를 표시함
  • 네이티브 및 npm 설치는 기본적으로 자체 업데이트함
    • Homebrew, WinGet, apt, dnf, apk는 기본 자체 업데이트 대상이 아님
    • Homebrew와 WinGet은 CLAUDE_CODE_PACKAGE_MANAGER_AUTO_UPDATE=1로 옵트인할 수 있음

배포 과정에 남은 신뢰 문제

  • 사람이 여러 에이전트를 동시에 감독하면 60초 안에 모든 질문을 확인하기 어렵고, 잘못된 선택 뒤 얼마나 많은 토큰이나 작업이 소비될지도 사전에 알 수 없음
  • Anthropic이 기능의 작성, 검토, 승인, 병합, 문서화, 릴리스 비교 단계에서 어떤 사람과 절차를 거쳤는지는 공개 기록으로 확인할 수 없음
  • 기능은 약 이틀 만에 적절한 옵트인 형태로 수정됐지만, 일일 출시 주기와 기본 자동 업데이트를 통해 예상 밖의 기능이 변경 로그 없이 배포될 수 있음이 드러남
  • 실행 파일 분석은 실제 배포 내용을 검증하는 유효한 우회 수단이지만, 정확하고 잘 편집된 변경 로그를 대체하지 못함
  • 악의를 전제할 근거는 없으며, 같은 종류의 일이 반복되는지가 이 배포 과정에서 무엇이 개선됐는지를 판단할 근거가 됨
Read Entire Article