← today i learned

기획과 실행을 분리하는 법

2026-07-26 · Claude Code 워크플로 · 이 시점의 스냅샷

코딩 에이전트와 일할 때 제일 오래 걸리는 건 코드가 아니라 "다음에 뭘 시킬지"를 정하는 일이다. 그래서 그 두 가지를 아예 다른 창에서 한다. 한 창에서는 할 일을 적기만 하고, 다른 창은 적힌 것을 실행하기만 한다. 이 글은 그 사이에 놓인 큐와 규칙을 정리한 것이다. 먼저 실제로 매일 쓰는 흐름을 통으로 보이고, 그다음에 그 흐름을 떠받치는 설비를 하나씩 본다. 열흘 남짓 더 굴리며 실측으로 뒤집힌 것들도 함께 넣었다.

분리. 세션 하나는 기획만(생산자), 다른 하나는 실행만(소비자) 한다. 둘은 서로 대화하지 않는다.

큐. 둘을 잇는 것은 저장소에 커밋되는 backlog/ 디렉터리다. 태스크 1건 = 마크다운 파일 1개.

손잡이. 그 파일을 사람도 에이전트도 손으로 고치지 않는다. 스킬(슬래시 커맨드)과 CLI 가 유일한 조작 수단이다.

불변식. 태스크는 구현 → 검증 → 커밋으로만 닫힌다. 검증 없이 커밋 없고, 커밋 없이 다음 태스크 없다.

비용. 비싼 건 병렬화가 아니라 위임 자체였다. 같은 작업을 리드가 직접 하면 $17.73, 워커에게 넘기면 $33.17.

1병목은 사람 쪽이다#

에이전트는 태스크 하나를 몇 분이면 끝낸다. 문제는 끝난 다음이다. "다음은요?"라는 물음에 답하는 동안 에이전트는 논다. 기획과 실행을 한 세션에서 하면 이 정지가 태스크마다 반복되고, 반대로 밀린 태스크를 붙잡고 있는 동안엔 다음 구상이 밀린다. 어느 쪽이 돌아도 다른 쪽이 멈춘다.

컨텍스트도 상한다. "무엇을 할지" 정하는 대화와 "어떻게 짤지" 정하는 대화는 필요한 맥락이 전혀 다른데, 한 창에 섞이면 서로를 밀어낸다. 다음 작업을 구상하려고 위로 스크롤하면 지난 태스크의 테스트 로그가 나오는 식이다.

그래서 물리적으로 창을 나눴다. 나누고 나면 사고 시간과 실행 시간이 겹친다. 다음 태스크를 궁리하는 동안 에이전트는 이미 적힌 것을 밀고 있고, 궁리의 재료는 대개 방금 그쪽에서 올라온 결과물이다. 이 겹침이 이 방식이 주는 이득의 전부고, 아래 나오는 큐·스킬·불변식은 전부 그 겹침을 안전하게 만드는 배관이다.

왼쪽에 도면과 스케치가 펼쳐진 작업대, 오른쪽에 공구와 부품이 정돈된 작업대가 있고, 두 작업대는 서로 닿지 않은 채 가운데 놓인 인덱스 카드 트레이로만 이어져 있다. 카드 몇 장이 왼쪽에서 트레이로, 트레이에서 오른쪽으로 흘러간다.
왼쪽은 기획, 오른쪽은 실행. 두 작업대가 직접 닿지 않고 가운데 트레이로만 이어진다는 것이 이 구조의 전부다. 삽화: Codex 이미지 생성

2하루는 두 손잡이로 돌아간다#

손잡이(슬래시 커맨드)는 열 개쯤 되지만, 실제로 하루의 대부분을 차지하는 건 두 개다. 생산자 창의 /add-task 와 소비자 창의 /loop-backlog. 나머지는 이 둘로 안 되는 경우를 위한 변주라, 먼저 이 흐름부터 통으로 본다.

왼쪽 창: 생각나면 적는다

뭔가 하고 싶어지면 그 자리에서 한 줄 친다. 코드는 열지 않는다.

/add-task GeekNews RSS 를 받아 관심사 프로필로 선별하는 아침 잡

그러면 스킬이 관련 파일과 선행 태스크를 가볍게 훑고 몇 가지를 되묻는다. 이 잡은 어떤 모델로 돌릴 것인가, 선별 건수는 고정인가 그날 재료에 맞추는가, 인기순과 관심사 중 무엇이 1순위인가. 답한 내용이 그대로 완료 조건(AC)이 되고, 태스크 파일 한 장이 큐에 떨어진다. 여기까지가 보통 2-3분이고, 이 창에서는 그것으로 끝이다. 구현은 시작하지 않는다.

이 단계에서 중요한 건 완결성이 아니라 또렷함이다. 나중에 실행할 세션은 지금 이 대화를 하나도 모르는 상태로 시작하기 때문에, 지금 머릿속에만 있는 판단(왜 그 모델인지, 어디까지가 범위인지)을 파일에 남겨 두지 않으면 그때 다시 정하게 된다. 그리고 다시 정하면 대개 다르게 정한다.

오른쪽 창: 틀어 놓고 지켜본다

큐에 몇 개만 들어가면 바로 튼다. 다 채워 두고 나중에 트는 것이 아니다.

/loop-backlog

인자는 없다. 소비자 세션이 알아서 큐를 조회해 착수 가능한 것(To Do 이고, 의존이 모두 끝났고, 막혀 있지 않은 것)을 골라 하나씩 완결한다. 태스크 하나의 처리는 늘 같은 모양이다. 상태를 In Progress 로 바꾸고, 관련 파일을 읽고, 판단이 필요한 갈림길이 있으면 스스로 정하거나 미뤄 두고, 구현하고, 완료 조건을 항목마다 실제로 확인해 체크하고, 전부 체크됐을 때만 커밋한다. 그리고 다음 태스크로 간다.

중요한 건 그동안 내가 노는 게 아니라는 점이다. 오른쪽에서 커밋이 하나씩 올라오면 그 중간 결과물을 보고, 보다 보면 다음에 할 일이 생긴다. 이 잡은 실행 시간이 4분이나 걸리네, 이 페이지는 진입점이 없네, 여기 실패하면 조용히 죽네. 그걸 그 자리에서 왼쪽 창에 /add-task 로 넣는다. 루프는 라운드가 끝날 때마다 큐를 처음부터 다시 읽으므로, 방금 넣은 태스크가 같은 실행 안에서 집힌다. 큐를 미리 다 채워 둘 필요가 없는 이유이자, 루프가 "남은 게 없을 때까지"를 판정할 때 빈 조회를 연속 두 번 확인하는 이유다.

그래서 실제 그림은 채우고 나서 비우는 것이 아니라, 두 창이 동시에 도는 것이다. 왼쪽은 오른쪽이 뱉은 결과를 재료로 다음 태스크를 만들고, 오른쪽은 왼쪽이 방금 넣은 것을 다음 라운드에서 집는다. 계획을 앞에서 다 세우지 않아도 되는 대신, 계획이 실행 결과를 따라 계속 갱신된다.

돌아보면 남는 것은 대개 커밋 목록이다.

720d02f [task-80] GeekNews 수집·선별 잡 구성
14d2c5f [task-85] 브리핑 발행 잡 구성 (렌더·push·cron)
fa96071 [task-86] Slack 발행 알림 추가
ef8d876 [task-87] 브리핑 진입점과 아카이브
fba26aa [task-84] 브리핑 소개 페이지
adfda30 [task-82] 액션 버튼을 til-inbox 접수로 연결

커밋 하나가 태스크 하나다. 각 커밋에는 코드뿐 아니라 그 태스크 파일의 변경(상태 전이, 체크된 완료 조건, 무엇을 어떻게 검증했는지 적은 노트)이 같이 들어 있다. 그래서 나중에 "이거 왜 이렇게 돼 있지"를 물을 때 커밋 하나만 열면 답이 나온다. 별도의 작업 일지를 쓰지 않는 이유다.

루프가 전부 처리하지 못하는 것도 있다. 내가 결정해야 하는 갈림길을 만나면 추측으로 밀지 않고 그 태스크를 Blocked 로 두고 "결정 필요: 무엇"을 노트에 남긴 뒤 다음 태스크로 넘어간다. 그래서 지켜보다 보면 한쪽에 결정 대기 목록이 모인다. 실패 더미가 아니라 내가 답해야 할 질문 목록이고, 그 답을 주는 것도 왼쪽 창에서 할 일이다.

두 창은 서로 대화하지 않는다

여기까지가 흐름의 전부다. 왼쪽은 쌓기만 하고 오른쪽은 비우기만 하며, 둘은 직접 이야기하지 않는다. 만나는 곳은 저장소 안의 파일뿐이다.

생산자 세션 기획만 한다 /add-task /add-draft /add-milestone 코드를 건드리지 않는다 backlog/ 태스크 1건 = 파일 1개 task-81 To Do task-80 To Do task-79 In Progress git 에 커밋되는 진실원본 소비자 세션 실행만 한다 /start-backlog /parallel-tasks /loop-backlog 큐를 새로 채우지 않는다 쌓는다 꺼낸다 상태 전이 · AC 체크 · 구현 노트를 같은 파일에 되쓴다 두 세션은 서로 대화하지 않는다. 만나는 곳은 파일뿐이다
생산자는 뒤에 쌓고 소비자는 앞에서 꺼낸다. 처리 결과가 다시 큐에 적히므로, 생산자는 소비자에게 묻지 않고도 진행 상황을 읽을 수 있다.

부수 효과 하나가 이 구조를 더 튼튼하게 만든다. 두 세션이 같은 디렉터리를 동시에 쓰는데도 충돌하지 않는다는 것. 생산자는 새 파일을 만들고 소비자는 기존 파일을 고치므로, 태스크 = 파일인 이상 겹칠 자리가 없다. 이건 지키라고 정한 규칙이 아니라 저장 형태에서 공짜로 따라 나오는 성질이다.

아래는 이 흐름을 떠받치는 설비들이다. 큐가 실제로 어떻게 생겼는지(§3), 두 손잡이로 안 되는 경우에 쓰는 나머지 손잡이들(§4·§5), 병렬로 돌릴 때 무는 값(§6), 커밋이 함부로 나가지 않게 하는 게이트(§7), 그리고 이 손잡이들 자체를 관리하는 방식(§8·§9). 흐름만 알고 싶었다면 여기서 §11(잘 안 맞는 곳)로 건너뛰어도 된다.

3큐의 실체, backlog/#

큐는 서버가 아니라 저장소 루트의 디렉터리다. Backlog.md 라는 오픈소스 도구가 구조를 정의하고 CLI 로 읽고 쓴다. 데이터는 전부 마크다운이라 GitHub 웹에서 그냥 읽히고, diff 가 곧 변경 이력이다.

backlog/
├── tasks/          # 실행 큐. 파일 1개 = 태스크 1건
├── drafts/         # 보류 아이디어. 실행 큐 밖
├── milestones/     # 태스크 묶음
├── docs/           # 배경·설정 같은 산문
├── decisions/      # 설계 결정 기록(ADR)
├── completed/      # 끝난 태스크 보관
└── config.yml

태스크 파일 한 장을 보자. 이 사이트 저장소에 지금 들어 있는 실물을 줄인 것이다.

---
id: TASK-80
title: GeekNews 수집·선별 잡 구성
status: To Do
created_date: '2026-07-26 03:55'
labels: [solo]
milestone: m-8
dependencies: [TASK-79]
priority: medium
---

## Description
RSS 를 받아 이미 처리한 항목을 state 로 걸러내고, 관심사 프로필과 함께
headless 세션에 넘겨 상위 N건 선별과 항목당 요약을 생성한다. 파일럿에서
항목 상세는 개별로 가져와야 밀도가 나왔다. 선별 후에만 상세를 가져오는
2단 구성으로 호출 수를 줄인다.

## Acceptance Criteria
- [ ] #1 cron 으로 매일 아침 1회 도는 run.sh 와 state 가 있다
- [ ] #2 RSS 수집 → 1차 선별 → 선별분만 상세 fetch 의 2단 구성으로 동작한다
- [ ] #3 신규 없음/실패 시 조용히 종료하고 로그를 남긴다
- [ ] #5 선별·정리 모델은 Opus 5 를 기본으로 쓴다 (경량 모델 아님)
- [ ] #6 선별 건수는 7-10건 범위에서 모델이 그날 재료에 맞춰 정한다
- [ ] #11 정렬은 추천 백분위가 1순위이고 관심사는 동률 보정에만 쓴다
       (파일럿 v6 에서 확정)

핵심은 frontmatter 가 아니라 Acceptance Criteria(완료 조건) 쪽이다. 이 체크박스들이 §7 에서 커밋 게이트가 된다. 그리고 #5 … (경량 모델 아님), #11 … (파일럿 v6 에서 확정) 같은 괄호가 이 방식의 성격을 잘 보여준다. 완료 조건은 "무엇을 하는가"만이 아니라 "어디서 멈추는가"이고, 때로는 "그 값이 어디서 나왔는가"다. 실행하는 쪽이 아무 맥락 없는 다른 세션이라, 경계를 적어 두지 않으면 선의로 넘어가고 근거를 적어 두지 않으면 합리적인 이유로 다른 값을 고른다.

같은 파일에 labels: [solo] 도 보인다. 병렬 실행에서 다른 태스크와 같이 돌리면 안 되는 것을 표시하는 자리다. 태스크를 적는 시점에 병렬 안전성까지 함께 적어 두면, 나중에 /parallel-tasks 가 그것을 판단하지 않고 조회한다.

상태는 기본 3종에 Blocked 를 더해 4종을 쓴다. 설정에서 두 가지를 더 바꾼다.

# backlog/config.yml (발췌)
statuses: ["To Do", "In Progress", "Done", "Blocked"]
auto_commit: false          # 커밋 시점은 도구가 아니라 스킬의 커밋 규칙이 정한다
check_active_branches: true # 다른 worktree 브랜치의 태스크 상태까지 읽는다
큐에 들어갔다고 다 실행되는 건 아니다

drafts/ 는 실행 큐 밖의 대기석이다. 소비자 스킬은 draft 를 아예 조회하지 않으므로, 설익은 착상이 실행 대상으로 새지 않는다. 착수를 결정하면 그때 인터뷰를 거쳐 태스크로 승격한다. 기록하는 문턱과 실행하는 문턱을 다르게 두는 장치다. 잃어버리기는 싫고, 전부 하고 싶지는 않으니까.

여기에 최근 규칙이 하나 붙었다. 착수 자체가 외부 조건에 막힌 일도 태스크가 아니라 draft 로 넣는다. 미출시 버전이나 상대방 응답 대기처럼 내가 무엇을 하든 지금은 시작할 수 없는 것들이다. 태스크로 만들면 자율 루프가 라운드마다 그것을 집었다가 Blocked 로 밀고 종료 보고에 계속 싣는다. "지금 착수는 가능한데 답이 필요한" 것과 "지금은 아예 시작할 수 없는" 것은 다르게 취급해야 노이즈가 줄었다.

4채우는 쪽, /add-task 와 그 이웃들#

생산자 세션이 하는 일은 §2 에서 본 그대로다. 아무 맥락 없는 소비자가 그대로 실행할 수 있을 만큼 태스크를 또렷하게 적는 것. 코드는 짜지 않는다. /add-task 로 안 되는 경우가 셋 있어서 손잡이가 넷이다.

스킬언제하는 일
/init-backlog 큐 자체가 없을 때 인터뷰로 계획을 세우고 backlog/ 를 초기화한다. 산문(배경·목표·제약)은 docs/ 로, 할 일은 태스크로. 구현은 시작하지 않는다
/add-task 할 일이 명확할 때. 가장 잦다 관련 파일·선행 태스크만 가볍게 훑고, 애매한 것을 인터뷰로 해소해 태스크 1건을 만든다. draft 의 승격도 여기서 한다
/add-draft 착상만 있을 때 drafts/ 에 무손실로 받아 둔다. 인터뷰도 완료 조건 강제도 없다
/add-milestone 덩어리가 태스크 여럿으로 쪼개질 때 마일스톤과 세부 태스크들을 인터뷰로 확정해 함께 만든다

무게중심은 /add-task 의 인터뷰다. 스킬이 묻는 것은 대개 완료 조건, 의존, 범위 경계이고, §3 예시의 "경량 모델 아님"이나 "파일럿 v6 에서 확정" 같은 괄호가 전부 거기서 나왔다. 여기서 5분 쓰는 것이 소비자가 엉뚱한 것을 30분 구현하는 것보다 싸다. 반대로 코드 조사는 일부러 얕게 한다. 깊은 조사는 실행 시점에 하면 되고, 기획 시점에 하면 큐에 넣는 문턱만 높아진다. 큐에 넣는 문턱이 높아지면 결국 안 적게 되고, 안 적으면 이 방식 전체가 성립하지 않는다.

"착수하지 않는다"는 당부가 아니라 권한이다

생산자 스킬 네 개는 모두 태스크를 만든 자리에서 멈춘다. 이 규율이 무너지면 "잠깐 이것만 고치고"가 시작되고, 기획 세션의 컨텍스트가 구현 로그로 오염된다. 애초에 나누려던 것이 도로 붙는 것이다. 그래서 이건 프롬프트 속 당부로 두지 않고 도구 권한으로 막았다. 네 스킬의 allowed_tools 에는 실행 계열 도구(서브에이전트 스폰·백그라운드 태스크)가 아예 없다. /add-draftAskUserQuestion 조차 없어서, "아이디어는 캐묻지 않고 그대로 받아 둔다"가 물리 법칙이 된다.

5비우는 쪽, /loop-backlog 와 그 이웃들#

고르는 기준은 In Progress 우선, 다음이 priority, 같으면 생성 순이다. 실행 형태는 셋 중 하나를 상황으로 고르는데, 태스크당 지키는 불변식(§7)은 셋이 같고 누가, 어디서, 얼마나 병렬로 도느냐만 다르다.

손잡이주체격리쓰는 상황
/start-backlog내 세션이 직접현재 브랜치 그대로지켜보며 순차 처리. 특정 번호만 찍어 돌릴 때도
/parallel-tasks백그라운드 팀원태스크별 worktree굵직한 독립 태스크가 2개 이상일 때만
/loop-backlog라운드 단위 자율솔로 기본기본. 틀어 놓고 결과를 보며 큐를 계속 채운다

이름이 바뀐 것을 눈치챘다면 정확하다. 원래는 /start-task·/loop-task·/next-task 였는데 전부 -backlog 접미사로 옮겼다. 레거시 PLAN.md 를 도는 /loop-plan 이 따로 남아 있어서, 손잡이 이름에 백엔드를 박아 두는 편이 헷갈리지 않았다.

/start-backlog 에서 하나 짚을 것은 시작 전 질문이다. 태스크에 안 적혀 있어 판단이 필요한 것(설계 방향, 라이브러리 선택, 범위 경계, 에러 처리 정책)은 구현 전에 모두 물어서 해소하고 추측으로 진행하지 않는다. 되돌리기 쉬운 사소한 선택(네이밍, 로그 문구)은 묻지 않고 정한 뒤 결과에 밝힌다. 기획 인터뷰가 아무리 촘촘해도 구현 층의 갈림길은 남기 마련이라, 게이트를 한 번 더 두는 것이다. 호출 인자도 세 가지를 받는다. 인자가 없으면 큐에서 알아서 고르고, 번호를 주면 그것만, 작업 서술을 주면 태스크로 먼저 등록한 뒤 진행한다. 큐를 거치지 않는 경로를 아예 없애 버리면 사람이 규칙을 우회하게 되므로, 우회로 자체를 큐 안으로 끌어들인 셈이다.

/parallel-tasks 는 태스크마다 팀원 하나를 붙여 각자의 worktree 에서 구현·커밋시키고, 검증을 통과한 브랜치만 순차 fast-forward 머지한다. PR 은 만들지 않는다. 팀원의 수정 허용 범위는 코드 + 자기 태스크 파일뿐이고, AGENTS.md·README.md 같은 공유 메타 파일은 리드가 머지 단계에서 일괄 처리한다. 여럿이 같은 파일을 만질 수 있는 유일한 경로를 미리 잘라 둔 것이다. 팀원의 "다 됐습니다" 자기보고는 그대로 믿지 않고, 브랜치와 커밋이 실제 존재하는지 리드가 확인한 뒤에 머지한다.

§2 에서 본 /loop-backlog 로 돌아오면, 그 자율성을 성립시키는 규칙이 세 겹 있다. 사람이 옆에 없는 동안 잘못 판단하면 그 결과가 라운드마다 번지기 때문에, 자율의 폭보다 자율의 경계를 먼저 정해 둔 것에 가깝다.

보조 손잡이 둘. /next-backlog 는 실행 없이 착수 후보만 추려 보고한다(큐만 읽고 코드·git·웹은 안 본다. "다음 뭐 하지"용 30초 조회). /cleanup-tasks 는 쌓인 Done 을 completed/ 로 옮겨 보드를 비운다. 압축하지 않고 통째로 옮기므로 구현 노트와 검증 기록은 그대로 남는다. 이건 사람이 부르는 일이 거의 없다. Done 이 일곱 개를 넘으면 /start-backlog 가 완료 보고 직전에 알아서 부른다.

6위임은 얼마짜리인가#

여기가 지난 판본에서 가장 크게 바뀐 부분이다. 병렬 실행을 두고 원래는 "토큰이 단일 세션의 3-10배"라고 적어 뒀었는데, 같은 태스크 셋을 세 방식으로 실제로 돌려 보니 그 숫자는 과장이었다. 2026-07-25 실측을 단가로 환산한 표다(측정 방법과 원자료는 에이전트 3명을 띄우고 1.36배를 얻었다에 따로 적었다).

$0 $10 $20 $30 병렬 팀 3명 서브에이전트 1명 순차 리드가 인라인으로 $33.17 $29.31 $17.73 캐시 write (프리픽스 재구성) 나머지
세 방식의 총비용과 그중 캐시 write 가 차지하는 몫. 병렬화가 무는 값(위 둘의 차이)보다 위임 자체가 무는 값(아래와 위의 차이)이 훨씬 크다.

병렬화 자체는 순차 서브에이전트 대비 1.13배로 거의 공짜였다. 진짜 격차는 위임하느냐 리드가 직접 하느냐에 있었고 그게 1.87배다. 차이의 정체도 한 항목으로 설명된다. 캐시 write, 즉 프리픽스 재구성이다. 팀원은 리드의 대화를 상속받지 못해 컨텍스트를 밑바닥부터 새로 캐시하지만, 리드는 이미 그걸 들고 있다.

아이소메트릭 시점으로, 나란히 선 세 개의 빈 작업 부스 앞에 똑같은 도구 상자 하나와 책 더미 하나가 한 벌씩 놓여 아직 들여지지 않은 상태이고, 오른쪽에는 공구판·부품 상자·조명·책이 이미 다 갖춰진 작업대 하나가 있다.
워커를 하나 띄울 때마다 같은 짐을 처음부터 다시 쌓는다. 그 반복이 위 표의 1.87배다. 삽화: Codex 이미지 생성

그래서 결론이 "병렬이 비싸다"에서 "위임이 비싸다"로 바뀌었다. 실무적으로는 반대 방향의 조정이다. 굵직한 태스크를 병렬로 돌리는 건 생각보다 싸니 겁낼 필요가 없고, 대신 단일 파일짜리 사소한 수정을 워커에게 넘기는 습관이 비싸다. 그건 리드가 인라인으로 처리한다.

속도 쪽 실측도 같이 붙는다. Opus 5 팀원 3명으로 잰 실효 동시성은 1.36배였다. 팀 구간이 19.2분인데 팀원들의 wall time 합이 26.2분이다. 이전 세대(opus 4.8/sonnet)에서 잰 약 1.8배, 최악의 경우 0.73배로 직렬보다 느렸던 것과 비교하면 세대가 올라가도 나아지지 않았다. cold-start 는 모델 능력이 아니라 구조적 비용이기 때문이다.

동시성을 깎은 범인은 모델이 아니라 태스크 크기였다

1.36배에 그친 이유는 태스크 세 개가 19.2분·3.7분·3.3분이었다는 데 있다. 짧은 둘이 끝난 뒤 구간의 3/4 을 한 명이 혼자 돌았다. 그래서 게이트가 하나 늘었다. 가장 큰 태스크가 나머지의 몇 배면 작은 것들은 팀원에 태우지 말고 리드가 인라인으로 가져간다. 팀원이 상위 모델로 돈다는 이유로 문턱을 낮추지도 않는다. 문턱을 낮출 근거는 모델이 아니라 태스크 크기 균질성이다.

같은 흐름에서 전역 지침에 상한이 하나 생겼다. 동시에 띄우는 팀원·서브에이전트는 5명까지. 지금 모델은 시키지 않아도 위임을 잘 하기 때문에, 필요한 것은 위임을 밀어 주는 문구가 아니라 상한이다. 더 돌릴 일이 남았으면 인원을 늘리지 않고 한 배치를 회수한 뒤 다음 배치를 띄운다.

추론 강도(effort)도 같은 자리에서 다뤄진다. 기본값은 medium 이다. Opus 5 에서는 low·medium 이 이전 세대 xhigh 급 결과를 내는 작업이 많아 비용 조절의 1차 레버가 강도 하향이 됐다. 설계 판단·원인 불명 디버깅·보안 리뷰의 재검증처럼 한 번 틀리면 되돌리는 비용이 큰 유형만 high 로 올리고, 정해진 치환이나 포맷 정리 같은 판단 없는 단계는 low 로 내린다. 다만 세션 중에는 자기 강도를 바꿀 수 없어서, 이 기준이 실제로 적용되는 곳은 스폰 경로뿐이다. 워커를 띄울 때 --effort 를 명시하거나 워크플로 스크립트에서 에이전트별로 지정하는 식이다. 일반 서브에이전트 도구에는 그런 노브가 없어서, 그쪽은 모델 티어로 강도를 가른다.

7불변식, 구현 → 검증 → 커밋#

어느 모드로 돌든 태스크 하나는 같은 순서로 닫힌다. 검증을 통과하지 못하면 커밋하지 않고, 커밋하지 않으면 다음 태스크로 가지 않는다. 이 방식에서 타협하지 않는 유일한 부분이다.

In Progress 착수 직전 전이 구현 작고 안전한 변경 검증 --check-ac N Done + 커밋 코드 + 태스크 파일 AC 가 하나라도 안 되면 커밋하지 않고 되돌아간다 전 항목 체크가 커밋의 전제. 통과 못 한 채 다음 태스크로 넘어가지 않는다.
완료 조건이 산문이 아니라 체크박스라서 "다 됐나"가 판단이 아니라 조회가 된다.

"검증이 기계화된다"는 말의 실체는 소박하다. 완료 조건이 태스크 파일 안의 체크박스이고, 에이전트가 항목을 실제로 확인할 때마다 CLI 로 체크한다는 것.

# 착수
backlog task edit task-12 -s "In Progress"

# AC 를 하나 확인할 때마다 (N 은 1-based)
backlog task edit task-12 --check-ac 1

# 전 항목 체크를 마쳐야 완료 전이
backlog task edit task-12 -s Done --notes "<무엇을·왜·검증 결과>"

그리고 커밋할 때 코드 변경과 그 태스크 파일의 변경(상태·AC 체크·노트)을 같은 커밋에 담는다. 사소해 보이지만 이 규칙이 방식 전체를 떠받친다. 커밋 하나가 "무엇을, 왜 했고, 무엇으로 확인했는가"를 통째로 들고 있어서 큐의 상태와 코드의 상태가 갈라질 수 없고, 별도의 작업 일지가 필요 없다. git 히스토리가 곧 일지다.

같은 워킹트리에 다른 세션이 붙어 있다

그 커밋을 만들 때 git add -A 를 쓰지 않는 것이 최근에 규칙으로 승격됐다. 옆 창에서 다른 세션이 같은 워킹트리를 만지고 있을 수 있기 때문이다. 실제 사고가 있었다. 문서 커밋 하나가 옆 세션이 진행 중이던 설정 변경과 새 태스크 파일을 통째로 담아 그대로 push 됐다. 증상이 전혀 없다는 게 고약하다. 커밋도 push 도 성공하고, 나중에 git log 로 남의 파일을 추적할 때에야 드러난다. 그래서 지금은 만진 파일만 경로로 명시해 add 하고, 커밋 직전에 git status 로 낯선 변경이 섞였는지 본다. 두 세션을 동시에 돌리는 구조라면 이건 선택이 아니라 필수다.

덤으로 세션이 소모품이 된다. 진행 상황의 진실원본이 전부 파일에 있으므로, 태스크 몇 개를 소화해 컨텍스트가 비대해진 세션은 커밋 직후 경계에서 버리고 새로 열어도 아무것도 잃지 않는다. 늘어진 세션을 붙들고 품질이 떨어지는 것보다 그쪽이 낫다.

중요한 설계 결정은 태스크 노트가 아니라 decisions/ 에 ADR 로 따로 남긴다(배경·결정·트레이드오프). 태스크는 완료되면 보관함으로 밀려나지만, 결정은 나중에 "왜 이렇게 돼 있지"의 답으로 계속 소비되기 때문이다.

8손으로 만지지 않는다#

여기까지 스킬 이름이 계속 나왔는데, 그게 이 방식의 숨은 절반이다. backlog/ 안의 파일을 손으로 열어 고치지 않는다. 추가는 /add-task, 실행은 /start-backlog. 에이전트도 파일을 직접 편집하는 대신 backlog CLI 를 부른다. 포맷 실수·ID 중복·상태 오타 같은 잔사고가 원천에서 사라진다.

더 큰 이득은 추상화다. 치는 명령이 큐의 백엔드와 분리돼 있어서, 백엔드를 갈아타도(실제로 한 번 갈아탔다, §9) 사용 패턴은 안 바뀐다. 스킬들이 backlog/ 존재 여부를 감지해 자동 분기하므로, 아직 옮기지 않은 옛 저장소에서도 같은 이름을 그대로 친다.

다만 CLI 도 전지하지는 않다

손잡이를 CLI 로 통일해도 그 CLI 가 모르는 구석이 있으면 사고가 난다. 실제로 ID 채번에서 났다. backlog 는 다음 번호를 고를 때 tasks/completed/ 만 본다. 그래서 archive/ 에 더 큰 번호가 있거나, 다른 머신이 만들어 push 한 태스크를 아직 안 당겼으면 이미 쓰인 번호를 그대로 재발급한다.

충돌 3건이 났고 전부 후자, 즉 여러 머신에서 같은 저장소의 큐를 채우다 생긴 것이었다. 수습이 비싼 이유는 시점 때문이다. 충돌이 드러나는 건 sync 할 때인데 그때는 이미 커밋 태그와 문서가 그 번호를 참조하고 있다. 지금은 태스크를 만든 직후 가드 스크립트를 1회 돌린다. 로컬의 tasks/·completed/·archive/ 에 더해 fetch 후 리모트 트리까지 훑어 진짜 다음 번호를 정하고, 방금 만든 태스크가 이미 쓰인 번호면 파일명과 frontmatter 의 id 를 그 자리에서 옮긴다.

bash ~/.claude/skills/references/backlog-id-guard.sh fix TASK-N
# ok=TASK-N       그대로 쓴다
# moved=TASK-N -> TASK-M   이후 명령·커밋 태그·의존 지정에 새 번호를 쓴다

여기서 얻은 일반론은 간단하다. 도구에 위임하면 도구가 보는 범위까지만 안전하다. 위임의 경계를 모르면 그 바깥에서 조용히 어긋난다.

9스킬 문서도 관리 대상이 된다#

이 방식이 굴러갈수록 손잡이(스킬 문서) 자체가 관리 대상이 됐다. 지금 backlog 계열 스킬만 아홉 개고, 전부 같은 전제를 앞에 이고 있었다. CLI 설치 안내, 조회에 --plain 을 붙이는 규칙, 모드 판별, 상태 4종. 하나 고치면 아홉 군데를 고쳐야 하는 상태였다.

그래서 공통 서문을 references/backlog-basics.md 한 장으로 빼고 각 스킬은 자기 고유 절차만 담은 뒤 그곳을 가리키게 했다. 코드에서 하던 것과 같은 일인데, 대상이 프롬프트일 뿐이다.

두 번째로 한 것은 점진 공개다. 스킬 본문은 호출될 때마다 통째로 컨텍스트에 올라가므로, 분기 하나에서만 쓰이는 절이 본문에 있으면 나머지 호출이 전부 그 값을 문다. 그런 절을 references/ 로 밀고 본문에는 맥락 포인터만 남겼다. 결과적으로 전체 스킬에서 150줄 초과가 사라졌다. 이 정비는 backlog 계열만의 일이 아니라 스킬 50개를 한 번에 훑은 작업의 일부였고, 그 전말은 스킬 50개를 Opus 5 기준으로 다시 쟀다에 있다.

스킬밀어낸 것
/herdr207132스폰 레시피 4종, 출력 형태 목록
/next-backlog153129인자 모드, 레거시 모드
/publish-til159146삽화 생성 레시피(삽화 분기만 도달)
/start-backlog130105레거시 PLAN.md 모드

무엇을 밀어내고 무엇을 본문에 남길지는 도달 확률로 갈랐다. 실패에서 유래했고 매 호출에 필요한 것은 본문에 남겼다. /herdr 의 함정 목록과 위 §6 의 비용 실측이 그런 예다.

세 번째 렌즈, 판단 위임

줄이는 기준도 하나 늘었다. 어떤 규정이 열거·수치·순서로 굳어 있을 때, 같은 결과를 "무엇을 달성하라"로만 서술해도 실행 시점의 모델이 스스로 도달하는가를 묻는다. 도달하면 규정을 지우고 목표만 남긴다. 규정은 모델이 그 목표에 이르는 다른 길을 막고, 상황이 규정의 전제를 벗어나면 조용히 어긋난 결과를 내기 때문이다.

남길 근거는 하나뿐이다. 관측된 실패. 모델이 그 목표 아래서 틀린 방향으로 간 것을 실제로 본 규정만 남기고, 그 관측을 규정 옆에 한 줄로 적어 둔다. 다음 사람이 같은 시도를 반복하지 않도록. 반대로 관측 없이 걷어내는 것도 같은 잘못이다. 행동 규칙·승인 게이트·안전 규칙은 애초에 이 렌즈의 대상이 아니다. 그것들은 모델의 판단을 이기려고 있는 것이라, 판단으로 대체 가능한지를 물을 자리가 아니다.

10지금 모습이 된 경위#

이 구조는 한 번에 설계된 것이 아니라 갈아타면서 남은 것이다. 경위가 방식 자체보다 유용할 수 있어 적는다.

1차, 마크다운 한 장

처음엔 저장소마다 PLAN.md 한 장을 두고 태스크를 전역 번호(T-1, T-2 …)로 쌓았다. 상태는 체크박스 표기, 완료 조건·의존·범위는 태스크마다 산문 필드로. 지금 구조의 뼈대(두 세션 분리, 생산자·소비자 스킬, 구현·검증·커밋 불변식)는 이 시절에 이미 다 있었다. 아픈 곳은 하나였다. 병렬로 돌리면 워커 여럿이 같은 파일 한 장을 고쳐야 해서, "워커는 플랜 파일에 손대지 말고 리드가 일괄 갱신한다" 같은 충돌 회피 규칙이 스킬 본문에 계속 자라났다.

2차, 전용 트래커 검토 후 반려

의존 그래프를 DB 에 넣는 이슈 트래커 계열 도구를 검토했다가 접었다. 그 도구들이 푸는 문제(수백 건 그래프 쿼리, 원자적 클레임)가 이 규모에선 발생하지 않았고, 무엇보다 진실원본이 DB 로 들어가는 것이 싫었다. 마크다운은 GitHub 웹에서 그냥 읽히고 diff 가 곧 리뷰인데, DB 는 열람에 도구를 한 겹 끼운다.

3차, 마크다운을 버리지 않는 도구

Backlog.md 는 질문을 어렵게 만들었다. 진실원본이 똑같이 git 안의 마크다운이라면 수제 체계를 기성품으로 바꿀 이유가 있나. 첫 검토의 결론은 "지금은 아니다"였다. 표준 필드가 이미 frontmatter 의 산문판이라 구조화 이득은 대부분 갖고 있었고, 잃을 것으로 꼽힌 건 파일 한 장 읽으면 프로젝트 서사가 복원되는 산문성이었다.

그 결론을 뒤집은 건 논리가 아니라 관찰이었다. 인간이 그 파일을 읽지 않더라는 것. 산문 서사의 실독자는 세션 리셋 후의 에이전트뿐이었고, 아무도 읽지 않는 산문은 매겨 둔 무게만큼 지킬 가치가 없었다. 반대로 칸반 보드는 인간이 실제로 소비하는 조망이다. 결정적 트리거는 "인간이 진실원본을 소비하지 못한다"였는데, 검토 때 세워 둔 재평가 트리거 세 개(병렬 상시화·규모·도구 혼용)는 전부 규모 이야기였고 이건 목록에 없었다.

트리거 목록의 함정

"이 조건이 되면 재검토한다"는 목록을 만들어 두는 건 좋은 습관이지만, 목록에 없는 것은 감시되지 않는다. 실제로 방아쇠를 당긴 조건은 목록의 셋 중 어느 것도 아니었다.

옮기고 실제로 좋아진 것

전 (PLAN.md 한 장)후 (backlog/)
완료 조건이 산문. "다 됐나"가 판단AC 체크박스. --check-ac N 조회가 된다
워커 여럿이 같은 파일을 고친다태스크 = 파일이라 충돌 면적 0. 회피 규칙이 통째로 사라졌다
완료가 쌓이면 한 줄로 압축. 검증 로그를 버린다completed/ 로 통째 이동. 정리와 보존이 양자택일이 아니다
우선순위가 서술 순서에 암묵적으로만priority 필드. 후보 산출이 추론이 아니라 조회
번호 관리가 부담이라 큰 태스크를 안 쪼갰다ID 자동 채번. 쪼개는 심리적 비용이 없다

스킬 본문도 가벼워졌다. 채번 규칙·상태 표준·ID 표기 보존처럼 도구가 없어서 프롬프트로 보완하던 규칙들이, 도구가 구조적으로 보장하자 backlog 경로에선 적을 필요가 없어졌다. 지금 /add-task 의 backlog 절은 "ID·접수일·상태는 도구가 채우므로 지정하지 않는다" 한 줄로 끝난다. 다만 정직하게 적자면 그 규칙들이 삭제된 건 아니다. 옛 저장소가 남아 있는 한 스킬은 두 모드를 겸해야 해서, 같은 파일의 레거시 절에는 T-N 전역 카운터와 표준 필드 규칙이 그대로 살아 있다. 가벼워진 것은 새 경로지 파일이 아니고, 파일은 오히려 두 세계를 다 이고 있었다. §9 의 점진 공개는 정확히 그 짐을 references/legacy-mode.md 로 내린 작업이다.

11잘 안 맞는 곳#

12치트시트#

하고 싶은 것세션손잡이
큐 자체가 없다. 계획부터생산자/init-backlog
할 일이 명확하다. 큐에 추가생산자/add-task <태스크>
착상만 있다. 잃지 않게만생산자/add-draft <아이디어>
덩어리를 여러 태스크로 쪼갠다생산자/add-milestone
다음 착수 후보만 본다양쪽/next-backlog
순서대로 직접 비운다소비자/start-backlog
독립 태스크를 worktree 병렬로소비자/parallel-tasks
틀어 놓고 알아서 비우게 한다소비자/loop-backlog
Done 이 쌓여 보드가 답답하다소비자/cleanup-tasks
PLAN.md 저장소를 옮긴다전환/migrate-to-backlog

한 문장. 저장소 안의 디렉터리를 두 세션 사이의 작업 큐로 삼아 한쪽은 계속 채우고 다른 쪽은 계속 비운다. 기획과 실행이 서로를 기다리지 않도록.