골인 지점을 옮겼다
EP 10 마지막에 이렇게 썼다.
"STATE B — 페퍼가 스스로 코드를 짜는 그 순간이, Phase 0의 진짜 골인 지점이다."
그 문장을 내가 뒤집었다.
STATE A 파이프라인을 완성하다가
triage가 6갈래로 나뉘면, 그 각각을 받아줄 손이 있어야 한다. 그 손들을 만드는 게 STATE A의 나머지였다.
store — Vault에 저장. extractor(Flash-Lite)가 "GOOGL 245달러로 저장해줘"에서 구조를 뽑고, vault-writer가 임베딩해서 넣는다.
lookup — 실시간 외부 정보. weather(wttr.in), calendar, web.search(Serper)를 툴 레지스트리로 묶었다.
task — 내가 할 일 등록.
여기까지는 됐다. 문제는 그다음이었다.
"날씨랑 환율 둘 다 알려줘" — intent가 두 개다. 이걸 순서대로 처리하려면 planner가 필요하다. 계획을 세우고, 단계별로 실행하고, 이전 단계 결과를 다음 단계에 넘기는($ref) 오케스트레이터.
planner 타입을 정의하고, createPlan/executePlan 스텁을 만들고, 멀티스텝 분기를 연결했다. 그리고 실제 실행부에 이렇게 써 넣었다.
if (triageResult.intents.length > 1) { responseText = '여러 가지를 한 번에 처리하는 기능은 곧 지원할게요!' }
stub. "곧 지원할게요."
멀티스텝 하나를 stub으로 두는 순간, 더 큰 질문이 따라왔다.
STATE B는 얼마나 남았나
STATE B는 페퍼가 없는 기능을 스스로 만드는 것이다. 코드를 생성하고, 격리된 환경에서 검증하고, 시스템에 자동 등록한다.
그 "격리된 환경"이 문제였다. E2B 같은 샌드박스를 세우고, 페퍼가 짠 코드를 안전하게 돌리고, 실패하면 3번 재시도하고, 그래도 안 되면 STATE C로 GitHub 이슈를 자동 생성하고 — 이걸 다 지으려면 몇 주다.
그 몇 주 동안 우리 가족은 페퍼를 못 쓴다.
여기서 비개발자의 유혹이 정확히 뭔지 알게 됐다. 가장 멋있는 걸 먼저 만들고 싶다. 페퍼가 스스로 코드를 짜는 장면은 이 프로젝트 전체에서 가장 짜릿한 순간이니까. EP 10에서 그걸 골인 지점이라고 부른 것도 그래서였다.
근데 출시되지 않는 제품은 제품이 아니다.
그래서 골인 지점을 옮겼다
결정을 문서에 박았다. context.md와 roadmap에.
STATE B/C는 Phase 2로 미룬다. Phase 0는 STATE A 완성으로 재정의한다.
Phase 0의 성공 조건이 바뀌었다. "페퍼가 스스로 코드를 짠다"에서 "가족이 매일 여는 앱이 된다"로.
이게 후퇴처럼 느껴졌다. 며칠 붙잡고 있었다. 근데 결정을 문서에 쓰고 나니까 이상하게 명확해졌다. EP 02에서 만든 그 문서 시스템이 여기서 또 일을 했다 — 내 머릿속이 흔들려도 문서가 잡아준다. 다음 세션의 나도, Claude Code도, 이 결정 위에서 흔들리지 않고 움직였다.
자가진화는 여전히 North Star다. 접은 게 아니라 순서를 바꾼 것이다. 지금 우리 가족에게 필요한 건 스스로 자라는 페퍼가 아니라, 오늘 아침 브리핑을 주는 페퍼다.
planner를 stub으로 남긴 것도 같은 결정
"곧 지원할게요"는 도망이 아니다. STATE C의 철학과 같다 — 못 하는 걸 못 한다고 정직하게 말하되, 그게 다음 진화의 입력값이 되게 둔다. 멀티스텝은 백로그에 들어갔고, 그 자리엔 정직한 안내 문장이 남았다.
한 번에 하나. 파일 하나, 함수 하나, Step 하나. 이 프로젝트의 규칙이 여기서도 통했다. 골인 지점을 옮기는 것도, 그 규칙의 일부였다.
이제 진짜로 만들 차례다 — 뇌가 아니라, 가족이 손에 쥘 앱을.