수정 6 · 단순화 6 · 리뷰 후속 4. 모두 머지됐다(영상 마지막 프레임 4:47 PM).
2026-09-23 · x.com
Opus 5.5로 Claude Agent SDK를 Lean으로 형식 검증했다
x.com · Boris Cherny · 2026-09-23
트윗의 "짧은 프롬프트 몇 개"는 영상에서 Slack 메시지 6개와 사람의 PR 승인·리뷰 코멘트였다. 증명은 3시간 만에 끝났지만 엔진 모델은 실제 실행에 맞춰 재작업됐고, 버그 24개 중 19개만 증명 출신이며, 증명이 낸 1차 수정은 리뷰에서 구멍 6개가 드러나 후속 PR 4개가 따라붙었다. "증명으로 단순화"는 삭제가 아니라 중복 병합이었다.
사람이 형식 검증 언어를 모르는 채로 짧은 지시만 주면, 모델이 실제 코드에서 상태기계를 모델링하고 증명해서 사람이 못 찾던 버그를 실제 수정 PR까지 끌고 갈 수 있는가.
Slack에서 사람이 준 것은 메시지 6개(검증해라 → PR 공개해라 → 같은 증명으로 코드 단순화해라 → 머지 걸어라 → 집계해라 → 인포그래픽 그려라)와 GitHub 승인·리뷰 코멘트뿐이다. 모델은 SDK와 claude.ts에서 상태기계 6개(stream, retry, turn loop, engine, session phase, SDK control)를 Lean 4로 옮겨 정리 1,359개를 sorry 없이 증명하고, 증명이 내놓은 반례를 실제 코드에 재현해 실패-후-성공 테스트가 붙은 수정 PR 6개를 냈다. 같은 증명이 "삭제할 분기는 거의 없고 중복 경로는 합칠 수 있다"는 근거가 되어 단순화 PR 6개, 리뷰 후속 PR 4개가 따라붙어 총 16개가 머지됐다. 최종 집계는 버그 24개(증명 발견 19 + 리뷰 발견 5), 비테스트 코드 순증 218줄, 테스트 케이스 +94.
sorry — Lean에서 "이 증명은 나중에" 하고 건너뛰는 자리표시자. "0 sorry"는 미완 증명 없이 전부 검사를 통과했다는 뜻이며, 이 글에서 완결성의 기준으로 반복해 쓰인다.lake build — Lean 프로젝트 빌드 명령. 빌드가 green이면 모든 정리가 커널 검사를 통과한 것이다.file:line을 인용한 명세와 각 모델의 가정·증명하지 않는 것이 함께 들어 있다.Boris Cherny(Anthropic, Claude Code)는 Opus 5.5에게 Claude Agent SDK와 claude.ts 상태 기계를 Lean 4로 검증시켜 PR 16개를 얻었다고 썼다. 첨부 영상(Slack 스레드 녹화)을 화면 타임스탬프대로 읽으면 그림이 달라진다. 증명은 약 3시간 만에 끝났지만, 6개 모델 중 엔진 모델은 실제 실행과 어긋나 "잠정"으로 시작해 실행 400건을 모두 받아들일 때까지 코드에 맞춰 재작업됐고, 버그 24개 중 증명 출신은 19개, 나머지 5개는 리뷰에서 나왔으며, "증명으로 단순화"는 코드 삭제가 아니라 중복 병합으로 귀결됐다(전체 비테스트 코드는 오히려 +218줄). 원문의 마지막 문장 "형식 검증이 코딩의 미래인가"는 주장이 아니라 질문이다.
수정 6 · 단순화 6 · 리뷰 후속 4. 모두 머지됐다(영상 마지막 프레임 4:47 PM).
증명이 찾은 19 + 리뷰에서 드러난 기존 버그 5. 여기에 1차 수정의 구멍 6개가 별도로 잡혔다.
인포그래픽 기준. 1:07 AM 감사 시점엔 1,359였다(아래 불일치 항목).
10:03 PM 첫 지시 → 다음 날 4:47 PM 전부 머지. 그중 약 11시간 47분은 사람 발화가 없는 공백이다.
영상에 보이는 사람의 Slack 발화는 여섯 개다. 그러나 Claude는 스레드에서 반복해 "Each one needs your approval before it can merge"라고 쓰고, "review comments"와 "two blocking review findings"에 맞춘 후속 PR을 낸다. 즉 사람의 개입은 프롬프트 6개 외에 GitHub 상의 PR 승인과 리뷰 코멘트를 포함한다.
시간 구조는 화면 타임스탬프로만 읽는다. 증명 단계는 10:03 PM → 1:07 AM(약 3시간 4분)에 끝났다. 1:12 AM 여섯 번째 PR 이후 12:59 PM까지 사람 발화가 없고, 오후는 공개·단순화·리뷰 대응·머지 대기로 흘러 4:47 PM에 "All 16 PRs are merged"로 닫힌다.
| 모델 | 정리 | 재생 | 수정 PR | 대표 버그 |
|---|---|---|---|---|
claude.ts stream | 219 | 6/6 | #72140 | 응답 중간에 본문이 깨끗이 끝나면 성공으로 처리, stop reason·비용 기록 없음(게이트웨이/3P 경로 필요) |
withRetry | 201 | 11/11 | #72145 | persistent 모드에서 429/529 대기가 다른 오류의 예산을 소진; Retry-After 하루짜리 503이면 하루를 통째로 잠 |
query.ts turn loop | 157 | 33/33 | #72130 | 잘못된 tool call 재시도와 max_tokens 절단 복구가 서로의 guard를 리셋 → 출력이 번갈아 나오면 무한 루프, --max-turns 1로도 안 멈춤 |
| engine turns | 319 | 400/400 | #72162 | 빌더 실패 후 flush 중 소비자가 스트림을 끊으면 턴이 cancelled로 보고되지 않음; 첫 next() 전 return(), 포기 후 늦은 send() 조용히 큐잉(저심각도: headless 세션은 uuid 없이 턴을 보내 SDK 소비자가 못 맞음) |
| session phase | 169 | 68/68 | #72128 | 샌드박스 네트워크 질의와 권한 프롬프트가 겹치면 둘 다 답해도 세션이 requires_action에 잔류, 때로 다음 턴 내내 |
| SDK control protocol | 294 | 2/2 | #72135 | 취소 후 재전달된 권한 요청이 아무도 취소 못 하는 핸들러를 남김; transport close가 throw하면 모든 awaiter가 고립 |
지켜진 것으로 보고된 불변식: 턴당 결과 하나·순서 보장·init 우선, idle 전에 result, 재시도 상한과 abort, 스트림은 닫힌 블록만 yield. 각 PR은 "수정 전 실패·수정 후 통과" 테스트를 동반한다.
main에 있었으나 증명이 아니라 사람 리뷰에서 드러난 것. 24개 안에 포함된다.따라서 "증명이 버그를 찾았다"는 절반이다. 증명 → 수정 → 리뷰 → 재수정의 두 번째 패스가 없었으면 수정 자체가 새 결함을 남겼다.
12:08 AM 게시물은 엔진 모델이 "실제 실행 몇 건과 아직 어긋나므로 증명을 잠정으로 보라"고 했다. 1:07 AM에는 "재작업된 엔진 모델이 400건 중 400건을 받아들인다"가 됐다. 모델이 실행을 거부하면 코드가 아니라 모델을 고친 것이다. 이는 형식 모델이 코드의 사후 서술이지 독립 스펙이 아니라는 뜻이고, 모델이 코드의 결함을 그대로 베꼈을 가능성을 증명 자체로는 배제하지 못한다는 뜻이다. 산출물 tarball에는 각 모델의 "가정과 증명하지 않는 것"이 별도 항목으로 들어갔다.
claude.ts의 복붙된 cleanup 경로 여러 개, query.ts에서 루프가 재시작되는 열 군데가 각각 상태를 손으로 재구성하는 구조.claude.ts −105줄), #72516 turn loop(continue마다 하던 guard 리셋을 테이블 하나로), #72518 SDK control(응답 write site 1개·cancel helper 1개). 테스트는 손대지 않았다.hasStandingPrompt()를 좁히는데, 리뷰 코멘트가 요구한 것보다 멀리 간다."형식 검증이 코딩의 미래인가"는 주장이 아니다. 영상이 실제로 뒷받침하는 범위는 좁고 구체적이다. 이미 존재하는 상태 기계 코드를 Lean으로 사후 모델링하면 동시성·재시도·상태 잔류형 버그 19개가 반례로 나왔고, 그 수정은 사람 리뷰 한 패스를 더 거쳐야 온전해졌으며, 증명은 코드를 줄이기보다 중복을 합치는 근거가 됐다. 이 범위를 넘어서는 일반화는 원문에도 영상에도 없다.
"the proofs show almost every flag and branch is reachable, so there's little to delete. What they do license is merging copies of the same logic into one." — Claude, 1:16 PM
독립 출처로 반증된 주장은 없다. 다만 핵심 성과(16 PR·24 버그·1,529 정리)는 Anthropic 내부 저장소와 Slack 화면에만 존재해 제3자가 재현할 수 없고, 영상 내부에서 정리 수가 두 값으로 갈리며, 같은 스레드의 형식검증 전문가(Hillel Wayne)는 "AI는 시스템 속성 도출에 매우 서툴다"는 반대 경험을 제시한다.
판정 어휘 정의: 사실 = 독립 출처로 확인 · 부분사실 = 핵심은 맞으나 세부 불일치 · 불확실 = 확인 수단 없음 · 내부정합 = 원문 자료 안에서만 확인 가능 · 의견 = 검증 대상 아님 · 거짓 = 반증됨
claude-opus-5-5, 입력 $4/출력 $20(백만 토큰), "Claude Fable 5.1 수준의 성능을 Opus 5보다 40% 낮은 비용으로"라고 적는다. 트윗 게시 시각(같은 날 23:39 UTC)은 출시 발표 당일이다. Claude Fable 5.1은 2026-09-01 출시된 상위 모델로 Anthropic 공식 페이지에 "Fable 5.1과 Mythos 5.1은 같은 모델, 다른 안전장치"로 설명된다. 공식 페이지에 Lean·TLA+·Agent SDK 언급은 없다. Anthropic: Opus 5.5 · TechCrunch · Anthropic: Fable 5.1anthropics/claude-agent-sdk-typescript(별 1.8k)는 예제·CHANGELOG·README만 담고 있고 src/가 없다. 영상에 등장하는 claude.ts·query.ts는 이 공개 저장소에도 anthropics/claude-code(별 약 148k, 역시 소스 없음)에도 없다. 따라서 영상의 "SDK 검증"은 공개 SDK 패키지가 아니라 그 아래 깔린 Claude Code 내부 상태기계(stream·retry·turn loop·engine·session phase·SDK control protocol)를 대상으로 한 것이다. Agent SDK 공식 문서 · GitHub: agent-sdk-typescript · GitHub: claude-codeclaude-code 704개, claude-agent-sdk-typescript 열린 PR 10개로 7만 번대에 이르지 못한다. claude-agent-sdk-typescript/pull/72128은 HTTP 404다. GitHub 공개 URL과 웹 검색 어디에도 해당 번호는 없다. 번호 규모로 보아 Anthropic 내부 모노레포의 PR이라는 것이 합리적 추정이며, 버그 내용·diff·머지 여부는 영상 밖에서 확인할 수 없다. pull/72128 → 404sorry, lake build, 표준 공리 3종(propext · Quot.sound · Classical.choice), #print axioms는 영상의 설명대로다.Classical.choice, propext, Quot.sound로 열거하고, sorry의 바탕이 되는 네 번째 공리 sorryAx에 대해 "완성된 증명에 나타나서는 안 되며 무엇이든 증명할 수 있다"고 명시한다. #print axioms는 "정의가 전이적으로 의존하는 모든 공리를 표시"해 sorry 의존을 감사하는 용도다. Lake는 Lean 4의 빌드 도구로 "lake build는 패키지(와 의존성)를 빌드한다"고 README가 적는다. 따라서 "1,529 theorems · 0 sorry · 세 표준 공리만"이라는 감사 기준 자체는 Lean 커뮤니티의 표준 관행과 일치한다(Anthropic의 페르마 정리 형식화 발표도 같은 기준을 썼다). Lean 레퍼런스: Axioms · Lake READMElake build·공리 확인) 장면이나 증가 사유가 없다. 트윗 본문은 정리 수를 언급하지 않으므로 트윗 자체의 오류는 아니고 첨부 자료 간 불일치다. Lean 레퍼런스: Axiomsrequires_action에 고착(#72128), 취소 후 재전달된 권한 요청이 취소 불가 핸들러를 남김(#72135), 잘못된 도구 호출 재시도와 max_tokens 복구가 서로의 가드를 리셋해 무한 루프(#72130). 각 PR에 "수정 전 실패·수정 후 통과" 테스트가 붙었다는 것도 Claude의 자기 보고다. 버그 자체는 공개 코드에서 재현할 수 없다. Agent SDK 문서Opus 5.5를 써서 Claude Agent SDK를 Lean으로 형식 검증했다. 짧은 프롬프트 두어 개 = 갖가지 버그와 레이스 컨디션을 고치는 PR 16개. 영상 첨부.
TLA+도 잘 통한다. 나는 가끔 Lean과 TLA+를 조합해서 데이터 흐름, 동시성, 상태 관리 주변의 문제를 찾는다.
두 언어 모두 잘 알지 못하지만, Claude는 둘 다 탁월하다. 이 접근은 코드를 형식적으로 모델링하고, 사람이라면 아마 잡아내지 못했을 버그를 찾는 데 대단히 유용하다.
형식 검증이 코딩의 (적어도 버그 찾기의) 미래일까?
Claude는 Slack 앱으로 등장하며, 봇 메시지마다 하단에 「Lens · Opus 5.5 · Configure」가 붙어 있다. 시각은 Slack 로컬(저자 시간대) 기준이다.
we have been getting lots of reports like this across cli and desktop <slack link> can you fuzz the cli + desktop apps to trigger de-auths? i think there's a bunch of bugs that can cause this even when the user should still be authenticated. fix any cases you can reliably repro (cli와 데스크톱 전반에서 이런 보고가 많이 들어오고 있어 <slack 링크> cli + 데스크톱 앱을 퍼징해서 de-auth를 유발해볼 수 있어? 유저가 여전히 인증된 상태여야 하는데도 이걸 일으키는 버그가 꽤 있을 것 같아. 안정적으로 재현되는 케이스는 고쳐줘)
큐에 쌓인 메시지가 모델에 전달되기 전까지 회색으로 표시되도록 하는 CLI UX 요청.
Use lean to verify the sdk and Claude.ts state machine (lean으로 sdk와 Claude.ts 상태기계를 검증해)
SDK와 claude.ts 상태기계를 Lean으로 검증하는 중
sorry 없음.같은 작업 목록의 이후 수정본:
sorry 없음.lake build 그린, 정리 1,359개 중 1,359개가 표준 공리 세 개 위에서 성립, sorry 없음. 차트와 소스는 아래에 게시했습니다.상태기계 여섯 개를 Lean 4로 모델링하고 증명했습니다 (sorry 없음). 증명이 만들어낸 반례는 모두 실제 코드에서 재현되며, 초안 PR 다섯 개가 이를 고칩니다. 각 PR에는 수정 전에는 실패하고 수정 후에는 통과하는 테스트가 붙어 있습니다:
requires_action에 남고, 때로는 다음 턴 내내 그 상태가 이어집니다.--max-turns 1로도 멈추지 않습니다.Retry-After가 하루인 503은 그 하루를 통째로 잠듭니다.성립한 것: 턴당 결과 하나, 순서대로, init이 먼저; idle 전에 result; 재시도 상한과 abort; 스트림은 닫힌 블록만 내보냄.
engine 모델은 아직 실제 실행 몇 건과 불일치하므로 그 증명은 잠정으로 봐 주세요. 마지막 증명이 끝나면 Lean 소스와 차트를 올리겠습니다.
이제 여섯 모델 모두 lake build로 빌드됩니다: 정리 1,359개, sorry 없음, 표준 공리 세 개만 사용, 그리고 모든 모델이 재생한 실제 실행을 전부 수용합니다 (재작업한 engine 모델은 이제 400건 중 400건). 타르볼에는 Lean 소스, 빌드 절차가 담긴 README, 각 전이마다 file:line을 인용한 모듈별 스펙, 그리고 각 모델의 명시된 가정과 증명하지 않는 것이 들어 있습니다.
| 모델 | 정리 수 | 실제 트레이스 재생 | 코드에서 재현된 발견 |
|---|---|---|---|
claude.ts stream | 219 | 6/6 | 8 |
withRetry | 201 | 11/11 | 3 |
query.ts turn loop | 157 | 33/33 (+5/5 literal, 2차) | 8 |
| engine turns | 319 | 400/400 | 3 |
| session phase | 169 | 68/68 | 4 |
| SDK control protocol | 294 | 2/2 | 4 |
범례: 증명된 불변식(보조 보조정리 포함) / 수정 증명(충분성 + 일치) / 반례 트레이스(정리) / 기타(파일 내 적합성 재생). Lean 4 커널이 검사한 정리 + 보조정리 (sorry 없음; 공리: propext / Quot.sound / Classical.choice만).
첨부: lean-verify.tar.gz
engine용 여섯 번째 초안 PR: #72162. 턴의 builder가 실패하고 그 뒤에 오는 flush 도중에 consumer가 스트림에서 빠져나가면, 그 턴은 한 번도 취소됐다고 보고되지 않았습니다. consumer가 첫 next() 전에 return()을 호출해도 같은 일이 벌어졌고, 버려진 뒤 늦게 도착한 send()는 조용히 큐에 쌓였습니다. 셋 다 먼저 main에서 재현했습니다 (새 테스트 8개 중 7개가 수정 전 실패, 수정 후 전부 통과). 심각도 낮음: headless 세션은 uuid 없이 턴을 보내므로 SDK 소비자는 오늘 당장 이 문제를 겪을 수 없습니다.
publush all prs (PR 전부 공개해)
여섯 개 모두 리뷰 준비 완료로 표시했습니다.
take that same proof and simplify the code (그 증명을 그대로 가져다 코드를 단순화해)
and mark all prs merge when ready (그리고 PR 전부 merge when ready로 표시해)
Lean 증명을 바탕으로 코드를 단순화하고, PR 전부 랜딩하는 중
이후 수정본:
여섯 개 모두 auto-merge가 켜졌습니다.
단순화에 관해: 증명에 따르면 거의 모든 플래그와 분기가 도달 가능해서 지울 것이 별로 없습니다. 증명이 허용하는 것은 같은 로직의 복사본들을 하나로 합치는 일입니다. 큰 것 둘은 claude.ts(복사-붙여넣기된 정리 경로 여러 개)와 query.ts(루프가 재시작되는 열 군데 각각이 상태 전체를 손으로 다시 만듭니다)에 있습니다. 모듈당 초안 PR 하나씩 진행 중이며, 각각의 수정 PR 위에 스택했습니다.
정정: turn-loop 수정 #72130은 아직 모든 종료가 터미널 메시지로 끝난다는 것을 보장하지 않습니다. 증명된 변형이 가정하는 것의 일부만 커버합니다. 나머지를 위한 후속 수정을 열겠습니다.
#72135 (SDK control)는 다시 승인이 필요합니다. main과의 충돌을 해소하면서 이전 승인이 무효가 됐기 때문입니다. 나머지 수정 PR 다섯 개는 머지됐습니다.
단순화 PR 세 개가 초안으로 올라와 있고, 테스트는 변경 없음:
stream, turn-loop, SDK-control 것은 뒤따릅니다. 리뷰 코멘트에 대한 수정도 초안으로 올라와 있습니다: #72499 retry, #72508 stream, #72509 session.
정정: #72135 (SDK control)는 결국 손댈 필요가 없습니다. 이전 승인이 새 커밋으로 이월되어 스스로 머지될 겁니다.
마지막 단순화 초안 세 개가 올라와 총 여섯 개가 됐습니다:
claude.ts에서 105줄 감소.continue마다 있던 재시도 가드 리셋이 이제 테이블 하나에서 나옵니다.turn-loop 리뷰 수정은 초안 #72515에 있으므로 리뷰 후속 네 개도 모두 올라왔습니다. 리뷰하실 때 볼 것 하나: session 후속 #72509는 hasStandingPrompt()도 좁히는데, 이는 리뷰 코멘트가 요청한 것보다 더 나간 변경입니다.
publish and mark merge when ready (공개하고 merge when ready로 표시해)
열 개 모두 merge-when-ready를 켜서 공개했습니다. 각각 머지되려면 승인이 필요합니다:
#72518은 #72135 위에 스택되어 있어, 그것이 랜딩되면 auto-merge를 켜겠습니다. 네 쌍이 같은 파일을 건드리므로 첫 번째가 머지되는 대로 각 쌍의 두 번째를 리베이스하겠습니다.
how many prs was that total? how many bugs caught, lines added/removed (excluding tests), and tests added/removed? (총 PR이 몇 개였지? 잡은 버그 수, 추가/삭제 라인 수(테스트 제외), 추가/삭제된 테스트 수는?)
PR 16개 집계 중
render it as a small infographic (작은 인포그래픽으로 렌더링해)
PR 16개, 버그 24개 수정: 증명이 찾은 19개와, 이미 main에 있다가 리뷰에서 드러난 5개. 리뷰는 제 1차 수정의 빈틈 6개도 잡아냈고, 후속 PR 네 개가 그것을 닫습니다. 테스트를 제외하면 코드는 순 218줄 늘었고 (+1,381 / −1,163), 단순화 PR 여섯 개만 놓고 보면 195줄을 줄입니다. 테스트 케이스 94개 추가, 9개 제거 (그중 8개는 재작성, 1개는 삭제).
첨부: 인포그래픽 「SDK + claude.ts 상태기계의 Lean 4 검증」 (아래 저자 답글의 이미지와 동일)
@boris #72515가 승인이 필요한 마지막 하나입니다. 새 head는 블로킹 리뷰 발견 두 건(이미지가 너무 큰 배치가 다음 프롬프트를 다시 실패하게 만들 수 있었음)을 고치고, #72516이 랜딩된 뒤 main을 머지해 넣었습니다. 리뷰는 깨끗하며 승인되면 스스로 머지됩니다. 16개 중 13개 머지됨; #72518과 #72508은 승인되어 랜딩 중입니다.
PR 16개 모두 머지됐습니다.
저자는 같은 스레드에 답글로 이미지 한 장을 올렸다: Opus가 인포그래픽을 만들었다

| 항목 | 값 | 비고 |
|---|---|---|
| 제목 | SDK + claude.ts 상태기계의 Lean 4 검증 | |
| 출하된 것 | 풀 리퀘스트 16개 | 5개 머지, 11개 오픈 |
| 풀 리퀘스트 | 16 | 수정 6 · 단순화 6 · 후속 4 |
| 수정된 버그 | 24 | 증명이 찾은 19개, 리뷰에서 5개 더; 거기에 1차 수정의 빈틈 6개를 리뷰에서 잡음 |
| 비테스트 라인 | +1,381 / −1,163 | 순 +218 |
| 테스트 | +94 / −9 | 추가/삭제된 테스트 케이스; 테스트 라인 +3,928 / −247 |
| Lean 모델 | 6개 | 정리 1,529개 · sorry 0 |
| PR 그룹별 비테스트 라인 — 수정(Fix) | PR 6개 −245 / +523 | 순 +278 |
| PR 그룹별 비테스트 라인 — 단순화(Simplify) | PR 6개 −741 / +546 | 순 −195 |
| PR 그룹별 비테스트 라인 — 후속(Follow-up) | PR 4개 −177 / +312 | 순 +135 |
참고로, 범용 서브에이전트에서 쿼터 사용량이 높게 나오는데 이를 조정하는 방법에 관한 문서가 거의 없네요. 가이드라인이 있나요?
Claude에게 서브에이전트를 덜 쓰라고 하거나, permissions에서 서브에이전트를 비활성화하라고 하라
그냥 라이브 스트리밍 해주시면 안 되나요, 어떻게 프롬프트하는지 구경하게 xD
위 영상이 나다
형식 검증 하는 사람으로서 내 경험상, AI는 고수준 시스템 속성을 생각해내는 데 정말 정말 정말 형편없었다(「초보」 수준 정도). 마지막으로 써본 게 Fable 5.1인데, Opus 5.5는 좀 나은가?
내 느낌으로는 훨씬 낫다. 다만 당신 생각이 궁금하다.
그나저나 Practical TLA+ 정말 재밌게 읽었다! 몇 년 전에 읽은 그 책이 이걸 시도해보게 한 계기였다
lake는 그 빌드 도구, sorry는 미완 증명 자리표시자.원문: Boris Cherny, X, 2026-09-23 08:39 KST · 한국어 번역·요약·팩트체크: dosi.dev