한 세션은 계획을 쌓고, 다른 세션은 계획을 비운다. Claude Code를 생산자·소비자로 나눠 쓰는 방법.
두 개의 Claude Code 창을 띄운다. 왼쪽 창은 "다음에 뭘 할지"를 계속 적고, 오른쪽 창은 적힌 걸 하나씩 구현·검증·커밋한다. 둘 사이를 잇는 건 저장소 루트의 PLAN.md 파일 하나다.
핵심은 두 역할을 물리적으로 다른 세션에 분리했다는 점이다. 계획을 세우는 머리(기획)와 코드를 짜는 머리(실행)는 필요한 맥락도, 사고 모드도 다르다. 한 세션에서 둘을 오가면 컨텍스트가 뒤섞이고 전환 비용이 든다. 파일 하나(PLAN.md)를 큐로 두면 두 머리가 서로를 기다리지 않고 각자의 속도로 돈다.
"계획하면서 동시에 구현하면 되지 않나?"에 대한 답.
가능은 하다. 하지만 한 세션에 기획과 실행을 몰아넣으면 세 가지 비용이 생긴다.
| 비용 | 한 세션 | 두 세션 |
|---|---|---|
| 컨텍스트 | 구현 로그·기획 메모가 한 창에 뒤섞여 길어진다 | 세션마다 한 종류의 맥락만 쌓여 깨끗하다 |
| 사고 모드 | "무엇을"과 "어떻게"를 계속 전환 | 기획은 넓게, 실행은 깊게 — 각자 한 모드 유지 |
| 흐름 | 기획하다 막히면 실행도 멈춘다 | 내가 다음 걸 기획하는 동안 에이전트는 이전 걸 계속 민다 |
/follow-plan으로 T-11을 구현·검증·커밋하는 몇 분 동안, 나는 기획 세션에서 T-14·T-15를 다듬는다. 에이전트가 손을 놓고 나를 기다리는 시간이 사라진다.
모든 게 이 파일 하나로 돌아간다. 세션은 서로 직접 말하지 않는다 — 오직 이 파일을 통해서만.
PLAN.md는 저장소 루트의 단일 마스터 문서다(파일명이 PLAN_<slug>.md 여도 된다 — 모든 스킬이 PLAN.md 우선, 없으면 PLAN_*.md 중 최근 수정본으로 폴백한다). 세션 리셋 후에도 이 파일만으로 작업을 재개할 수 있을 만큼 상세하게, 그러나 큐로 쓸 수 있을 만큼 구조적으로 적는다. 항목은 세 계열로 번호를 매긴다.
중요한 규칙 하나: T만 소비 대상이다. I(아이디어)는 "언젠가 할 수도 있는" 대기석이라, /follow-plan 같은 소비자 스킬은 이걸 건너뛴다. 어설픈 착상이 곧바로 구현 큐로 새는 걸 막는 안전장치다. 착수하기로 하면 그때 인터뷰를 거쳐 정식 T 태스크로 승격한다.
[x]로 닫히면서 병합 커밋 SHA가 함께 박혀 있다.
## 다음 할 일 (백로그)
- [x] T-1 rc/aliases 죽은 kmon alias 제거. 완료(머지 b8f4c3d)
- [x] T-3 usage-brief.py 버그 2건 수정. 완료(머지 f1abe17)
- [x] T-4 스킬 미러 재동기화 (codex/gemini). 완료(머지 ae7855d)
- [x] T-7 usage-brief 공통 모듈 추출. 완료(머지 bd8e92c)
- [ ] T-10 (blocked — 사용자 승인 후) 노출성 정리.
## 아이디어 (보류 — 착수 전 상세 인터뷰)
(I-N 으로 모음. follow-plan 진행 대상 아님.)
여기서는 코드를 거의 짜지 않는다. "다음에 뭘 할지"를 큐에 정확히 적어 넣는 게 전부다.
생산자 세션에서 가장 많이 쓰는 건 /add-plan이다. 떠오른 작업을 인자로 던지면, 스킬이 가벼운 조사만 하고(관련 파일·선행 태스크 확인 정도) 애매한 점을 AskUserQuestion으로 물어 정리한 뒤, 다음 T 번호를 붙여 백로그 맨 아래에 append한다. 구현은 시작하지 않는다 — 그건 소비자의 몫이다.
태스크 본문에는 표준 필드 네 가지 — 완료 조건:(검증 가능한 기준) · 의존:(선행 태스크, 없으면 없음 을 반드시 명시) · 범위 힌트:(변경 대상 파일) · 접수:(날짜) — 를 적는다. 소비자 스킬이 이 필드로 검증 기준을 잡고, 특히 의존: 없음 명시는 /sub-follow-plan·/parallel-plan이 추측 없이 병렬 안전성을 판단하는 근거가 된다.
| 스킬 | 언제 | 결과 |
|---|---|---|
| /make-a-plan | PLAN.md가 아직 없을 때 — 파이프라인의 시동 | 배경·목표·제약·태스크 목록을 갖춘 PLAN_<slug>.md 마스터 문서 신규 작성 후 멈춘다 — 구현은 소비자 몫. (add-plan은 PLAN이 없으면 이걸 자동 호출한다) |
| /add-plan | 지금 당장 할 일이 명확할 때 | 다음 T-N을 표준 필드와 함께 백로그에 추가. I-N의 태스크 승격(I → T)도 담당 — 원래 아이디어에는 → T-M 편입 마킹. 가장 자주 쓰는 손잡이 |
| /add-idea | 착상은 있는데 아직 확정 아님 | I-N으로 보류 섹션에 기록. 인터뷰·구현 없음 — 손실 없이 받아만 둔다 |
/make-a-plan도 /add-plan도 /add-idea도 코드를 건드리지 않는다 — make-a-plan 은 마스터 문서를 만든 뒤 그 자리에서 멈춘다. 생산자 세션의 임무는 소비자가 맥락 없이도 실행할 수 있을 만큼 태스크를 또렷하게 적어두는 것뿐이다.
큐에 쌓인 T 태스크를 꺼내 구현·검증·커밋으로 완결한다. 상황에 따라 세 가지 소비 모드가 있다.
모든 소비자 스킬은 같은 불변식을 지킨다: 구현 → 검증 → 커밋을 한 태스크 단위로 완결하고, 검증을 통과하지 못하면 커밋하지 않으며, 커밋 없이 다음 태스크로 넘어가지 않는다. 다른 건 얼마나 병렬로, 어디서 실행하느냐다.
| 모드 | 실행 주체 | 격리 | 병렬 | 고를 때 |
|---|---|---|---|---|
| /follow-plan | 내 세션이 직접 | 인플레이스 (현재 브랜치) | 순차 | 기본값. 태스크가 서로 얽혀 있거나 내가 흐름을 눈으로 따라가고 싶을 때 |
| /sub-follow-plan | 리드(세션 모델)가 하위 모델 워커에 위임 | 인플레이스 | 라운드 병렬 | 끝까지 자율로 밀되, worktree까지는 필요 없을 때. 판단은 보수적으로 하고 질문은 모아 마지막에 |
| /parallel-plan | 백그라운드 팀원들 | worktree (태스크별 분기) | 진짜 병렬 | 서로 의존 없는 굵직한 태스크가 2개 이상일 때. 병렬 이득이 작으면 스스로 솔로로 폴백 |
두 세션이 시간축 위에서 어떻게 맞물리는지.
/make-a-plan으로 마스터 문서를 연다./add-plan으로 T를 쌓는다. 아직 애매한 건 /add-idea로 I에 던져둔다./follow-plan(또는 상황에 맞는 변종)으로 큐 앞에서부터 하나씩 완결한다./cleanup-plan으로 압축하고, 다음에 뭐 할지 헷갈리면 /next-plan으로 후보를 뽑는다.두 세션이 같은 PLAN.md를 동시에 쓴다. 뒤에 저장한 쪽이 덮어쓸 것 같은데, 실제로는 거의 부딪히지 않는다. 왜?
답은 둘이 파일의 서로 다른 구역을 건드리기 때문이다. 생산자는 백로그 끝에 새 줄을 붙이고(append), 소비자는 이미 있는 태스크의 체크박스를 뒤집고 진행 로그를 남긴다. 물리적으로 겹치는 라인이 거의 없다.
이 "구역 분리"는 규칙이라기보다 각 스킬의 동작에서 자연히 따라 나온다. /add-plan은 정의상 맨 아래에 다음 번호를 붙이고, 소비자 스킬은 자기가 집어든 태스크 줄만 완료로 바꾼다. 게다가 소비자는 태스크마다 곧바로 커밋하므로, 변경이 파일에 오래 떠 있지 않고 빠르게 git에 확정된다.
채우고 비우는 것 말고, 큐 자체를 관리하는 두 손잡이.
플랜 파일만 훑어 "다음에 착수 가능한 T 후보"를 추려 보고한다. 코드·git·웹은 안 본다. 진행 중 항목을 맨 앞에 두고, 의존이 안 풀린 건 막힘으로 표시. 소비를 시작하기 전 "뭐부터 하지?"를 정리하는 데 쓴다.
완료된 T 태스크의 상세 구현 로그·검증 기록을 - [x] T-12: 한 줄 요약 형태로 압축한다. 미완료·보류·설계 결정은 그대로 보존. PLAN.md가 비대해져 큐로서의 가독성이 떨어질 때 돌린다 — 소비자 스킬은 완료 태스크가 3개 이상 쌓이면 종료 보고에서 이 스킬 실행을 권장한다.
"마크다운 플랜 파일은 원시적이지 않나? 전용 도구를 쓰지 그래?"에 대한 답. 대표 주자인 beads와 비교해 본다.
beads(bd)는 정확히 이 파이프라인이 다루는 문제 — "AI 코딩 에이전트가 장기 작업에서 컨텍스트를 잃지 않게 하기" — 를 겨냥한 의존성 그래프 기반 이슈 트래커다. 마크다운 TODO/플랜 파일을 대체하겠다고 명시적으로 선언하는 도구이고, 실제로 잘 만들어져 있다. 이슈가 그래프의 노드, 의존이 엣지가 되고, 임베디드 SQL 데이터베이스(Dolt)에 저장된다.
| beads가 잘하는 것 | 어떻게 |
|---|---|
| 착수 가능 태스크 선별 | bd ready — 열린 블로커가 없는 태스크만 쿼리로 골라준다. 의존 그래프가 깊어도 즉답 |
| 컨텍스트 절약 | 에이전트가 플랜 전체를 읽는 대신 bd show <id>로 필요한 이슈만 꺼내 읽는다. --json 구조화 출력 |
| 동시성 | bd update --claim으로 할당+상태를 원자적으로 클레임. 해시 기반 ID(bd-a3f8)라 병합 충돌이 없다 |
| 기억 관리 | bd remember로 프로젝트 메모리를 쌓고 bd prime이 에이전트 시동 컨텍스트로 주입 |
| 감사 추적 | 모든 변경이 DB 커밋으로 기록된다 |
그런데도 이 파이프라인은 마크다운 PLAN.md를 쓴다. beads가 열등해서가 아니라, beads가 푸는 문제가 이 규모에서는 아직 발생하지 않기 때문이다.
beads의 핵심 가치는 쿼리 가능한 의존 그래프다. 태스크가 수백 건이고 블로킹 관계가 얽혀 있으면 "지금 착수 가능한 게 뭔가"는 쿼리 없이 답하기 어렵다. 하지만 이 파이프라인의 백로그는 사실상 선형 큐다 — 태스크 수십 건, 블록된 항목은 한 줄짜리 "blocked (T-x 대기)" 표기로 충분한 수준. /next-plan이 플랜 파일 하나를 훑는 것으로 몇 초 만에 같은 답을 낸다. 그래프 DB가 풀어줄 병목이 없다.
beads가 마크다운의 약점으로 꼽는 두 가지가 이 파이프라인에서는 운영 습관으로 막혀 있다.
| 마크다운의 약점 | beads의 해법 | 이 파이프라인의 해법 |
|---|---|---|
| 파일이 계속 자라 컨텍스트를 잡아먹는다 | DB에 넣고 필요한 이슈만 꺼내 읽기 | /cleanup-plan — 완료 태스크를 한 줄 요약으로 접어 파일 자체를 작게 유지 |
| 동시 편집이 병합 충돌을 낸다 | 해시 ID + 셀 수준 머지 | 구역 분리(§7) — 생산자는 끝에 append, 소비자는 기존 줄의 체크박스만. 2세션 규모에선 겹칠 라인이 없다 |
반면 beads로 갈아타는 순간 생기는 비용은 규모와 무관하게 즉시 발생한다.
bd 바이너리와 임베디드 Dolt DB(.beads/)가 모든 작업 머신에 깔려야 한다. 지금은 git과 텍스트 에디터면 끝이다.PLAN.md는 GitHub 웹에서 그대로 읽히고 diff가 리뷰가 된다. beads는 issues.jsonl 내보내기가 있지만 어디까지나 사본이고, 열람·검색·이력 확인에 도구가 한 겹 낀다./add-plan·/follow-plan·/next-plan·/cleanup-plan이 전부 "마크다운 파일을 읽고 고친다"를 전제한다. 큐의 포맷을 바꾸면 생산자·소비자 양쪽 손잡이를 다 새로 깎아야 한다.PLAN.md는 큐인 동시에 사람이 읽는 상태 문서다. "현재 상태" 요약과 디자인 결정의 근거·트레이드오프가 산문으로 함께 산다. 이슈 레코드로 쪼개면 이 서사가 필드 사이로 흩어진다.역할별로 어떤 손잡이를 언제 당기나.
| 하고 싶은 것 | 세션 | 스킬 |
|---|---|---|
| 플랜 자체가 아직 없다 | 생산자 | /make-a-plan <작업 설명> |
| 지금 할 일이 명확하다 — 큐에 추가 | 생산자 | /add-plan <태스크> |
| 착상만 있다 — 나중에 볼 것 | 생산자 | /add-idea <아이디어> |
| 뭐부터 할지 후보만 보고 싶다 | 생산자 | /next-plan |
| 큐를 순서대로 직접 실행 | 소비자 | /follow-plan |
| 끝까지 자율로 밀되 위임 | 소비자 | /sub-follow-plan |
| 독립 태스크를 병렬로 밀기 | 소비자 | /parallel-plan |
| 완료 기록이 쌓여 비대해짐 | 소비자 | /cleanup-plan |
PLAN.md를 두 세션 사이의 작업 큐로 삼아, 한쪽은 /add-plan으로 계속 채우고 다른 쪽은 /follow-plan·/parallel-plan으로 계속 비운다 — 그래서 기획과 실행이 서로를 기다리지 않는다.