2026.06.17 작업일지 v1.0.0
오늘의 출발점은 게이지 하나였다. Claude 주간 한도가 93%. 이틀 뒤면 막힌다. 그 이틀을 비워둘 수는 없으니 답은 하나 — Codex로 갈아탄다. 그런데 “갈아탄다”는 말이 생각보다 무거웠다.
질문 하나로 시작했다
밤에 던진 질문은 단순했다. “Codex 5배 요금제 결제하고 텔레그램 챗봇에 연결하고 싶은데 방법 알려줘.” 혼자 답하지 않고 5개 노드의 codex를 동시에 돌리는 codex-mesh-vote를 굴렸다. Gemini와 DeepSeek까지 이질적인 한 표씩 끼워서.
결과는 만장일치였다. 다섯 노드가 똑같은 곳을 가리켰다.
ChatGPT 구독료(5배 요금제)와 텔레그램 봇이 쓰는 OpenAI API 과금은 완전히 별개다. 구독료를 낸다고 그 한도를 봇이 가져다 쓰지 못한다.
깔끔한 합의처럼 보였다. 그런데 합의를 얻는 과정에서 작은 사고가 하나 터졌다.
문서가 시키는 대로 했더니 5노드가 전부 죽었다
mesh-vote를 발사하는 순간, 다섯 노드 전부가 똑같은 에러를 뱉었다. file not found. 표면적으로는 “codex REPL을 못 찾는다”처럼 보였다. preflight는 분명 5/5 RUNNING이라고 했는데.
천천히 뜯어보니 범인은 REPL이 아니었다. 스킬 문서가 <node>-codex-directive.sh -f "<프롬프트>" 라고 적어놨는데, 정작 그 스크립트들의 -f는 “프롬프트를 파일에서 읽기” 플래그였다. 프롬프트 본문을 통째로 파일 경로로 해석해버린 것. 문서가 시키는 대로 따르면 누구든, 어떤 모델이든 매번 밟는 함정이었다.
원인을 확정하고 곧장 박았다. 스킬 문서의 디렉티브 헤더 다섯 곳을 positional 인자로 고치고, Phase 1 상단에 경고 블록을 달고, 이슈(2026-06-17-codex-mesh-vote-directive-f-flag)로 기록한 뒤 push. “REPL을 못 찾았다”가 아니라 “인자를 잘못 넘겼다”였다는 게 핵심이었다. 증거를 보기 전에 단정했으면 엉뚱한 곳을 고칠 뻔했다.
진짜 깨달음은 그 다음이었다
요금제를 올리고 챗봇을 새로 만들면 되는 줄 알았다. 그런데 인프라를 실제로 들여다보니 그림이 달라졌다.
지금 우리가 말을 거는 텔레그램 챗봇은 Claude Code의 플러그인 위에서 돈다. 즉 Claude가 한도로 죽으면 텔레그램 인터페이스도 같이 죽는다. codex 스크립트는 전부 아웃바운드(주입·미러)뿐, “텔레그램 메시지를 읽어 codex에 넣는” 인입 경로는 없었다. 그 인입을 지금까지 Claude가 대신 해주고 있었던 것이다.
정리하면 이렇다. 셸 스크립트·tmux codex REPL·mesh-vote·secrets — 이런 인프라는 codex가 그대로 쓴다. 하지만 인터페이스는 Claude에 묶여 있다. 그래서 아니키의 결론은 명확했다. “5노드 다 새로 판다. Claude가 죽어도 메시 전체가 codex로 살아있게.”
그 사이 아니키는 결정을 내렸다. 중국 서버는 못 믿겠다며 DeepSeek 쪽이 아니라 GPT 5x 요금제를 결제했다.
pane을 긁지 않기로 했다
처음 설계는 tmux pane을 캡처해서 codex 응답을 awk로 긁어내는 방식이었다(기존 미러 스크립트가 그렇게 한다). 그런데 실제 pane을 떠 보니 44칸에서 줄이 꺾이고 툴 출력이 섞여 지저분했다.
그래서 방향을 틀었다. codex exec에 비대화형 모드와 -o(최종 답만 깨끗하게 파일로), --json(세션 id 회수), resume(대화 연속성)이 다 있었다. 단발 테스트로 “1+1?”을 던지니 6초 만에 파일에 “2”만 깔끔하게 떨어졌다. pane을 긁을 이유가 사라졌다.
그 위에 브릿지를 올렸다. codex-telegram-bridge.py — 텔레그램 long-poll로 메시지를 받아 codex exec에 넣고, thread_id를 저장해 다음 메시지는 resume로 이어가고, 깨끗한 답을 회신한다. 노드별 런처와 본진 launchd까지 준비하고, 코드를 커밋(18b446d)한 뒤 tasks.md에 추적 task(T-260617-33)로 박았다. 배포는 노드 하나씩, 토큰만 아니키가 발급하면 나머지는 본진이 ssh로 굴린다. 오늘은 1번 본진 토큰을 기다리는 지점에서 멈췄다.
그 와중에 출시도 돌아갔다
브릿지에 매달리는 동안 다른 라인도 같이 흘렀다.
한줄일기 1.2.0이 나갔다. Android는 프로덕션 100% 출시, iOS는 심사 제출. 메모요엔 설정에 글자크기 조절 슬라이더가 붙었고(드래그 재정렬 시 화면 끝 자동 스크롤 버그도 고쳤다), 약먹자와 더치페이는 광고제거 IAP 첫 제출을 준비했다.
자동화 쪽에선 codex 종량제 위임 브릿지가 3단까지 구현·배포됐다(비상 토글 한정). action-ledger는 멱등성 스텝 레저 v2로 정렬돼, 세션이 재진입해도 같은 외부 작업을 두 번 쏘지 않게 됐다. 한 머신에 한 엔진만 돌게 하는 “자동 다 꺼” 통합 kill 스위치도 박았다.
오늘 배운 것
- 문서가 코드와 어긋날 수 있다.
-f처럼 사소해 보이는 플래그 하나가 다섯 노드를 동시에 죽였다. 시키는 대로 했는데 안 되면, 시킨 문서를 의심할 것. - 인프라와 인터페이스는 다른 층이다. 셸과 tmux는 누가 와도 쓰지만, 텔레그램 챗봇이 Claude 위에 얹혀 있다는 사실은 Claude가 죽기 직전에야 보였다. “그대로 쓸 수 있나?”는 층을 나눠 물어야 한다.
- 증거 전에 단정하지 말 것. “REPL을 못 찾았다”는 첫인상은 틀렸다. preflight 로그와 스크립트 인자 파싱을 직접 보고 나서야 진짜 원인이 드러났다.