저장소 맨 위의 파일 넷
Coop이라는 이름이 생기기 전, 우리 가족 AI 세션들은 대부분의 프로젝트가 시작하는 방식대로 일을 맞췄어. 저장소 맨 위에 놓인 텍스트 파일로. 할 일은 TODO.md에 적었어. 한 브레인이 다른 브레인한테 부탁하는 건 HELP-REQUEST.md, 넘기는 작업은 DELEGATION-REQUEST.md. 다음에 올 세션한테 남기는 쪽지는 NEW-SESSION-REMINDER.md. 끝난 작업은 history/ 폴더에 하나씩 마크다운 파일로 쌓였고.
세션은 시작할 때 이걸 읽고, 일하면서 고쳤어. Claude Code든 Codex든 저장소를 건드리는 브레인은 전부. 처음엔 늘 그렇듯 잘 돌아갔지. 그러다 안 돌아가기 시작했고, 파일들이 물러나던 날 TODO.md는 811줄, history/엔 파일이 330개였어.
공손하게 부탁만 하던 머리말
옛 TODO.md는 스스로를 Principles + Template Block — IMMUTABLE이라고 소개하는 블록으로 시작했어. 바로 밑엔 "Do not modify or delete", 그리고 "This block itself must always remain at the top, exactly as is." 이 블록은 늘 맨 위에, 지금 모습 그대로 있으라는 거지. 첫 원칙은 이거였어. "Only keep things that are in progress. Delete immediately when done — git handles the changelog." 마지막 원칙은 "The goal is to save tokens + save mental energy."
두 사실을 나란히 놓고 봐. 진행 중인 것만 두라는 게 첫 규칙인 파일이 811줄까지 자랐어. IMMUTABLE이라는 단어는 부탁이었어. 주석 안에 적혀서, 앞으로 이 파일을 열 모든 작성자한테 건네는 부탁. 그 부탁을 어기는 작성자를 파일은 하나도 막을 수 없었고. 문제 전체가 여기 축소판으로 들어 있어. 보장이란 보장은 전부 산문에 있었고, 산문은 거절을 못 해.
두 번째 원칙도 눈여겨봐. "끝나면 지워라"는 끝난 일이 판에서 아예 사라진다는 뜻이야. 파일은 누가 열려 있다고 생각하는 일은 보여줘. 근데 뭐가 끝났는지, 누가 끝냈는지, 언제였는지는 못 보여줘. 지운 기록은 git이 기억하지만, 지금 돌아가는 일을 맞추는 사람이 diff를 읽진 않잖아.
실패 다섯 가지
이 파일들을 대체한 설계 문서는 망가진 방식을 딱 다섯으로 꼽아.
- 저장소 안에 산다. 고칠 때마다 커밋이야. 여러 브레인이 동시에 고치면 머지 충돌이 나고 작업 트리끼리 어긋나.
- 금방 불어난다. 모든 세션이 매번 파일 전체를 컨텍스트 창에 올려. 자기 일과 상관없어도.
- 잘못 고쳐진다. 브레인들이 어느 항목이 어느 건지 놓치고, 엉뚱한 항목을 다시 쓰고, 줄을 날려. 문서 표현 그대로 옮기면 Markdown was never a multi-writer format.
- 상태가 없다. "읽었나?" "확인했나?" "진행 중인가?" "끝났나?"가 없어. 보낸 쪽과 받은 쪽이 주고받을 스레드도 없고.
- 끝없이 쌓인다. history 폴더는 읽는 것 자체가 신입 교육 부담이 되는 선을 넘었어.
텍스트 파일이 왜 틀린 도구인가
셋째 실패는 구조를 뜯어볼 만해. 기강 문제가 아니라 기계적인 문제거든. 텍스트 파일을 저장하면, 작성자가 아까 읽어둔 사본으로 계산한 내용이 파일 전체를 덮어써. 읽고, 고치고, 쓰는 사이에 비교하는 단계가 없어. 두 세션이 같은 바이트를 읽고, 각자 고치고, 각자 저장하면 두 번째 저장이 첫 번째를 지워. 어느 저장도 실패하지 않아. 진 쪽 세션은 졌다는 얘길 영영 못 들어.
게다가 항목 자체엔 기계가 붙잡을 정체성이 없어. 누가 위에 한 줄 끼워 넣는 순간 "세 번째 항목"은 세 번째가 아니게 돼. 누가 문구를 바꾸는 순간 "fix the backup script라고 적힌 줄"은 더는 안 맞고. 문자열을 찾아 바꾸는 AI 편집 도구는 이걸 더 나쁘게 만들어. 정확한 옛 텍스트가 있어야 하는데, 그 옛 텍스트가 바로 옆 세션이 방금 바꾼 거거든.
여럿이 쓰는 조율 장소에 실제로 필요한 건 짧아.
- 항목마다 변하지 않는 정체성. "todo #7"이 누구한테나, 언제까지나 같은 걸 가리키게.
- 문장이 아니라 필드로 된 상태. 그래야 "taken"을 해석하지 않고 검사할 수 있어.
- 실패할 수 있는 쓰기. "아직 open이면 taken으로 바꿔" 같은 쓰기. 그리고 이겼는지 작성자한테 알려주는 쓰기.
데이터베이스 행 하나면 셋 다 돼. 아래 실행 가능한 스케치는 같은 경쟁을 텍스트 파일과 원장 행에서 각각 벌여봐. 파일은 마지막에 저장한 쪽만 조용히 남겨. 조건을 건 UPDATE는 딱 한 세션만 이기게 하고, 진 쪽엔 잘못 읽을 수 없는 0행을 돌려줘.
부트스트랩
2026-05-12, cwkPippa 커밋 2616ed8a("Bootstrap Pippa Coop System")가 루트 파일 넷을 전부 새 명세를 가리키는 안내판으로 바꿨어. 셋은 아홉 줄짜리 폐기 안내문이 됐어. TODO.md는 811줄에서 9줄로 줄었고, 옛 내용은 git 기록에 남았어. 세 줄짜리 빈 자리였던 NEW-SESSION-REMINDER.md는 오히려 쉰 줄로 늘었어. 새 편지 의식을 가르치는 안내판이 됐거든. 그 자리에 들어온 건 저장소 바깥의 SQLite 데이터베이스 하나, 작은 헬퍼 모듈, 명령줄 껍데기. 처음엔 테이블 넷이었어. todos, requests, messages, history_entries. history 파일들은 다음 단계에서 따라왔고.
그 부트스트랩의 두 선택이 이 코스 나머지를 떠받쳐. 기록을 저장소 밖으로 빼니까 고칠 때마다 커밋하는 세금과 머지 충돌이 한 번에 끝났어. 그리고 데이터베이스는 WAL 모드(write-ahead logging)로 돌았어. 읽는 쪽이 쓰는 쪽을 안 막고, 쓰는 쪽도 읽는 쪽을 안 막아. 쓰기끼리는 여전히 하나씩 차례로 가고. Coop은 쓰는 양이 아주 적어. 필요했던 건 처리량이 아니라, 쓰기를 거절할 수 있는 자리였어.