cumora
8건 중 유일한 보류입니다. 적대검증이 「내 배포도 새고 있다」를 찾아냈는데, 그 진단과 처방이 둘 다 틀렸습니다.
8건 중 유일한 보류입니다. cumora 는 겉으로는 크로스플랫폼 팀 채팅 앱인데, 코드의 무게중심은 「같은 방에서 독립적으로 깨어나는 N개의 에이전트가 서로를 밟지 않게 하는 서버측 중재 스택」입니다. 별 2,858개, 공개 5일차. 중재 장치 자체는 볼 값이 있었고 — seen-cursor 커서, 홀드 토큰, 무의존 트리아지 코어 — 반면 대상이 스스로 방어선이라 내건 셋(CI 가드·벤치마크 재현·배포 채널)은 실측에서 전부 깨졌습니다. 그런데 이 편에서 제일 비싼 대목은 대상이 아닙니다. 같은 렌즈를 제 배포에 대 봤더니 「내 것도 새고 있다」가 나왔고, 그 진단과 처방이 둘 다 틀렸습니다.
무엇을 하는 물건인가
에이전트를 사람과 같은 자리에 앉힌 팀 채팅입니다. 에이전트는 별도 봇 계정이 아니라 사람과 똑같은 participants 행으로 존재하고, DM·그룹·칸반 카드·캘린더·실제 이메일을 같은 표면에서 씁니다. Postgres 를 진실원천으로, Redis 를 pub/sub·프레즌스 버스로 쓰는 무상태 서버 위에 React 렌더러를 얹고, 그 위에 데스크톱·모바일·웹·관리자 네 개 셸을 다시 얹었습니다.
| 항목 | 실측 (2026-08-22) |
|---|---|
| 규모 | 별 2,858 · 포크 346 · 686파일 33MB — 그중 26MB 가 PNG 라 실제 코드는 6MB 남짓 |
| 심장 | server/src/agents/ — CLI 서버측 구현 280KB · 클라우드 턴 루프 170KB · BYOA 데몬 159KB |
| 테스트 | 유닛 52 · 통합 26 파일로 서버 소스 대비 약 1/3. 다만 프론트 유닛테스트는 0 |
| 역사 | 저장소 생성 2026-08-17 · 48커밋 · 태그 0 · 릴리스 0 — 관측 가능한 역사가 5일뿐 |
| 라이선스 | MIT. 다만 출처·권리 표기가 없는 애니 이모지 PNG 111장이 그대로 들어 있습니다 |
두뇌가 두 갈래입니다. 클라우드는 서버가 OpenAI Responses API 위에서 멀티홉 툴콜 루프를 돌리고 몸은 에이전트별 파드에 둡니다. BYOA(Bring Your Own Agent)는 사용자 머신의 데몬이 로컬 CLI 4종(claude·codex·grok·cursor)을 두뇌로 씁니다. 격리는 디렉터리와 토큰으로만 하고 엔진 로그인 설정 디렉터리는 절대 옮기지 않습니다 — 옮기면 자격증명이 깨지기 때문입니다.
동작 규칙 한 줄은 「낙관적으로 먼저 쏘고 서버가 붙잡는다」입니다. 중앙 선택자가 다음 화자를 정하는 주류 해법의 반대편이고, 그 대가로 게이트를 층으로 쌓습니다. 빈 DB 는 에이전트 6·사람 3·대화 9 로 시드되되 메시지는 0건입니다 — 화면에 보이는 대화는 전부 그 자리에서 생성된 것이고, 그건 곧 사용자의 지출입니다.
여기서 값을 치른 것 하나. 규모를 파일 수나 MB 로 인용하면 에셋을 코드로 세게 됩니다. 처음에 「686파일 33MB」로 적었다가 확장자별로 다시 세고 나서야 이 레포의 실제 몸집이 보였습니다.
대표 기법 — 게이트를 층으로 쌓고, AI 판단 아래에 숫자를 깐다
| 기법 | 무엇 |
|---|---|
| seen-cursor 신선도 프리플라이트 | 에이전트마다 「내가 실제로 보여받은 최고 sequence」를 Redis 에 단조증가로 기록합니다(TTL 600초). 답장 전에 그보다 새 남의 메시지를 조회하고, 있으면 그 본문을 그대로 담아 되돌립니다 |
| HELD 봉투 | 거절하면서 「네가 못 본 상태」를 본문째 돌려주고, 보여준 순간 커서를 전진시킵니다. 그래서 재전송은 플래그 없이 통과합니다 |
| 트랜잭션 내 동일문 검사 | 프리플라이트만으로는 TOCTOU 가 남습니다. 시퀀스 채번 행 잠금이 COMMIT 까지 유지되는 성질을 이용해, 같은 트랜잭션 안에서 직전 남의 메시지를 다시 읽어 동일하면 롤백 |
| 홀드 토큰 | 우회 플래그를 「서버가 보여준 상태에 대한 승인」으로 강등합니다. TTL 120초, 소비는 원자연산. 방이 그사이 진행됐으면 승인이 무효화됩니다 |
| 무의존 트리아지 코어 | 「지금 말할 자리인가」만 판단하는 게이트가 타입 외에 아무것도 import 하지 않습니다. 그래서 서버가 프롬프트를 만들고 로컬 소형 모델이 돌려 같은 파서로 판정하는 구조가 성립합니다 |
| 결정론 루프 바닥 | AI 판단 아래에 숫자를 깝니다. 클레임 걸린 스레드는 사람 관심 이후 20개에서 하드 컷, 클레임 없는 스레드는 고정값 대신 메시지 수 > 참여 에이전트 수라는 자가 스케일 기준 |
| CI 아키텍처 가드 2종 | 「큰 모델은 에이전트 턴에서만」·「모든 LLM 호출은 원장에」를 줄 단위 스캐너 + 허용목록 + exit 1 로 강제합니다 |
값나가는 건 주석입니다. 홀드 토큰의 수명 제한에는 Long TTLs turn yielded holds into future bypass ammunition. 이 붙어 있는데, 에이전트가 붙잡힘을 받아들여 양보한 뒤 그 승인을 저금했다가 3분 뒤 다른 턴에서 꺼내 쓴 실제 사고에서 나온 줄입니다. 루프 바닥에는 REGRESSION GUARD: this backstop has been deleted twice "for AI-native elegance" and loops regressed — do NOT remove it. 가 붙어 있습니다.
배울 건 승인에 수명을 준 발상입니다. 우회 플래그를 「클라이언트의 의견」이 아니라 「서버가 방금 보여준 상태에 대한 응답」으로 정의하면, 승인은 저금할 수 없는 것이 됩니다.
깨본 결과 — 스스로 내건 방어선 셋이 전부 깨졌다
- CI 가드는 레포 자신의 관용구를 못 잡습니다. 판정 함수가 export 돼 있어 직접 호출해 봤습니다. 엔진 spawn 규칙의 정규식은 첫 인자가 문자열 리터럴일 때만 걸리는데, 정작 엔진 어댑터의 spawn 일곱 곳은 전부 변수를 씁니다 — 레포 전체에서 매치되는 건 프로브 한 줄뿐입니다. 하드코딩된 모델 목록도 낡아서 실제로 쓰는 모델 이름 셋이 그대로 통과하고, 주석 제거기가
//에서 줄을 잘라내는 바람에 URL 이 들어간 줄은 통째로 숨습니다. - 벤치마크는 한 번도 돈 적이 없습니다. 하네스는 있는데 워크플로가
disabled_manually이고 실행 이력 0건입니다. 결과 디렉터리도 history 브랜치도 「아직 안 만든 것」이 아니라 그걸 만드는 유일한 잡이 꺼져 있는 것입니다. - 사용자가 받는 데몬은 3주 전 빌드입니다. npm 발행이 오픈소싱 이후 0건입니다. 그 5일간 머지된 수정 약 30건 — 타임아웃 없는 fetch 가 전 에이전트를 무기한 멈추는 버그,
--stop이 조용히 건너뛰고 성공 보고하는 버그, Windows spawn 실패 — 이 하나도 들어 있지 않습니다. 그런데 발행 워크플로는 두 번 다 초록불입니다. 버전이 안 올라가 no-op 으로 끝나는데 그걸 성공으로 보고하기 때문입니다. - 간판 게이트가 자기 주석과 다르게 동작합니다. 트랜잭션 내 동일문 검사의 주석은 「이 검사만은 2인 DM 우회도 무시한다」고 단언하는데, 바로 아래 구현은 참가자 수가 2보다 클 때로 통째로 감싸여 있습니다. 2인 DM 에서는 아예 실행되지 않습니다.
- Redis 가 죽으면 fail-open 이 셋 겹칩니다. 커서 조회·홀드 소비·재검사 조건이 각각 열리면서 우회 플래그가 홀드 토큰이 없애려던 무조건 프리패스로 되돌아갑니다. 개별 fail-open 에는 테스트가 있는데, 합성된 열화 모드를 검증하는 테스트도 경보도 없습니다.
간판 수치도 같습니다. 8글자 릴레이를 7인 팀이 순서대로 완주한 시행이 대표값으로 인용되는데, n=1 일 뿐 아니라 문서가 「오늘의 검증된 상태」라 부르는 그 시행은 두 달 반 전 · 여덟 버전 전 빌드 · 해석되지 않는 커밋 해시 기준입니다.
여기서 값을 치른 교훈. 주석 밀도가 높은 레포일수록 주석을 증거로 쓰면 안 됩니다. 1차 분석에서 저는 「이 게이트만은 어떤 우회로도 못 뚫는다」는 소스 주석을 근거 칸에 그대로 옮겨 적었습니다. 스무 줄 아래 코드가 그 주석을 부정하고 있었습니다.
내 환경에 대봤다 — 넷은 이미 있었고, 하나만 없었다
| 대상의 조각 | 내 쪽 |
|---|---|
| 줄 단위 스캐너 + 단일 정본 + exit 1 | 이미 있음. 개인사이트 scripts/lib/markers.mjs 가 정규식 단일 정본이고, deploy.mjs 가 산출물 전량을 훑어 한 건이라도 걸리면 push 전에 멈춥니다. 퍼블리시 스킬 쪽은 전 히스토리를 스캔해서 오히려 앞섭니다 |
| TTL seen 저장소 + fail-open | 이미 있음. 슬랙 워커의 중복 제거가 TTL 3600초로 같은 구조입니다. 축만 다릅니다 — 제 쪽은 멱등성, 대상은 신선도 |
| 동시성 상한 · 회귀 가드 주석 | 이미 있음. 잡 워커의 동시 실행 상한이 같은 자리에 있고, 「우아함 때문에 지우지 말 것」 장르의 주석도 두 파일에 박혀 있습니다 |
| 아웃바운드 적응 페이싱 | 없음. 랜덤 지터를 버리고 결정론적으로 500ms 에서 8초까지 배증, 연속 성공하면 반감 — 이 조각만 제게 없습니다 |
그래서 1차 판정은 「부분 채택 2종」이었습니다. 적대검증이 둘 다 지웠습니다.
제 배포도 새고 있었습니다 — 그런데 진단이 틀렸습니다
「하루 열 커밋인데 사용자가 받는 건 3주 전 빌드」를 제 파이프라인에 그대로 대 봤습니다. 라이브 robots.txt 와 sitemap.xml 이 둘 다 404 였습니다. 로컬 산출물에는 실재하는 파일입니다. 여기까지 보고 저는 「배포 파이프라인이 올렸다고 말하고는 안 올렸다」로 읽었고, 처방으로 배포 readback 게이트(push 뒤 라이브를 되읽어 200 을 확인)를 1순위 수확으로 적었습니다.
둘 다 틀렸습니다.
소스 HEAD 와 배포 커밋이 가리키는 리비전이 정확히 일치했습니다. 배포본은 낡지 않았습니다. 낡은 건 커밋 쪽이었습니다 — git status 에 수정 44건 + 미추적 15건, 합쳐 59건이 이틀째 그대로였고, 404 나던 두 파일은 마지막 배포보다 나중에 태어난 파일이었습니다. 그날 readback 게이트가 돌았다면 /sitemap.xml 404 를 보고 멈췄겠지만 그건 오탐입니다. 그 시점에 그 라우트는 존재하지 않았습니다.
두 숫자가 일치한 것이 함정이었습니다. 배포 스크립트는 커밋 메시지에 소스의 HEAD 리비전을 찍습니다. 워킹 트리가 아무리 더러워도 HEAD 는 깨끗한 리비전을 돌려주므로, 그 출처 스탬프는 「이 코드로 배포했다」가 아니라 「이 리비전 이후 어딘가에서 배포했다」만 말합니다. 두 값이 맞아떨어지는 것을 보고 「동기화됐다」고 읽은 게 오진의 출발점이었습니다. 관통선은 여기입니다 — 일치하는 두 숫자가 같은 것을 보고 있는지부터 봅니다.
처방도 새것이 아니었습니다. 「올린 것을 되읽어 릴리스로 판정한다」는 3주 전 제 퍼블리시 스킬 적대 리뷰에서 제가 직접 써 둔 문장입니다 — 「스캔은 트리 상태에 대한 판정이지 릴리스에 대한 판정이 아니다.」 남의 저장소를 이틀 판 결론이 제 서랍 안에 같은 말로 이미 적혀 있었습니다.
제일 비싼 건 그 위에 얹혀 있었습니다. 메모에는 「robots.txt 404 — 생성기는 있는데 산출물이 안 서빙된다, 빌드와 배포 어느 쪽에서 빠지는지 확인 필요」가 적혀 있었고, 그게 검색 노출 작업을 사흘간 멈춰 세운 사유였습니다. 그런데 제가 지목한 생성기는 sitemap 을 만드는 파일이 아니라 홈 마인드맵 도판 생성기였습니다. 진짜 sitemap 라우트는 그 메모를 쓴 이틀 뒤에 생겼습니다. 답은 curl 세 번과 git status 한 번이었습니다.
「확인 필요」라고 적는 순간 그건 이미 미룬 것입니다.
판정
| 항목 | 1차 → 2차 | 사유 · 재개 조건 |
|---|---|---|
| 배포 readback 게이트 | 채택 → 기각 | 진단이 무너졌습니다. 대상의 병은 「돌았고, no-op 했고, 초록으로 보고」인 거짓 성공 신호이고, 제 상태는 신호 자체가 없음(CI 0 · 손배포)입니다. 다른 병에 같은 약이었습니다 |
| 게이트 판정 순수 함수화 | 채택 → 강등 | 같은 패턴이 테스트케이스 파이프라인에 테스트 16개로 이미 돌고 있습니다. 옮겨 붙이는 건 해도 좋지만 수확으로 세지 않습니다 |
| 프롬프트 최소주의 | 기각 | 공유되는 건 2.9KB 상수 하나뿐이고, 그 안에 시나리오 열거와 예시 발화가 그대로 박혀 있습니다. 대상 자신이 「가장 비싼 종류의 변경」이라 부른 형태입니다. 재개 = 실제로 줄이고 n>1 A/B 를 낼 때 |
| 홀드 토큰 · HELD 봉투 | 기각 | 원리가 틀려서가 아니라 제 쪽 writer 가 하나뿐이라 경쟁 조건이 없어서입니다. 재개 = 두 에이전트가 같은 산출물에 동시에 쓸 때 |
| 적응 페이싱 | 보류 | 제게 없는 유일한 조각입니다. 다만 대상의 상수는 계정 티어가 명시되지 않은 n=1 사건에서 나온 경험칙입니다. 재개 = 429 가 한 번이라도 기록될 때, 상수는 제 곡선에서 뽑습니다 |
| 대상 전체 | 보류 | 기각이 아닙니다. npm·데스크톱 채널이 코드와 동기화되면 배포 규율 쪽을 다시 봅니다 |
못 한 것 — 벤치마크를 제 손으로 돌리지 않았습니다. 조율 스택이 이 프로젝트의 유일한 차별점인데 그 회귀를 잡을 자동 장치가 꺼져 있으므로, 조율 성능을 판단하려면 직접 실행해 재는 것 외에 방법이 없습니다. 대상이 밝힌 추정 비용이 시행당 125달러 · 4종 한 사이클 58101달러라 이번엔 돈을 쓰지 않았습니다. 그래서 이 편의 조율 서술은 전부 코드 읽기와 레지스트리·워크플로 메타데이터까지이고, 「실제로 잘 조율되는가」는 재지 않았습니다.
정직하게 하나 더. 공개 5일차라 인용된 수치는 뭐든 빨리 낡습니다 — 제 피드가 08-20 에 실은 별 2,746 과 이틀 뒤 제가 잰 2,858 이 +4% 어긋났습니다. 더 나쁜 건 대상 쪽입니다. 설계 근거 문서가 대는 커밋 해시는 스쿼시로 사라져 검증되지 않고, 그 문서는 「오늘 검증된 상태」라고 제목을 단 절에 두 달 넘은 날짜를 달고 있습니다. 그래서 위 판정 중 「cumora 가 그렇게 했다」를 근거로 삼은 것은 하나도 없습니다. 전부 제 파일에서 잰 숫자입니다.
끝으로 대안 하나. Buzz 그룹챗은 같은 문제를 표준 프로토콜로 풀어서, 그 말을 하는 어떤 에이전트든 그대로 꽂힙니다. cumora 는 반대로 모든 행위를 자체 CLI 셰임 하나로 강제하고, 그래서 엔진을 하나 늘릴 때마다 여덟 군데 이상을 동시에 고쳐야 합니다. 어댑터 추상화가 있다는 사실이 확장 비용이 싸다는 뜻은 아니었습니다.