발명한 게 아니라 — 옮겨 온 거야
interactive Claude Code 가 임의로 긴 작업을 살아남는데, 그 이유가 핵심 전부야: 그 resume 은 chat-log replay 가 아냐. state 를 읽어.
- TodoWrite = item 마다 status 가진 명시적 persist 된 step 리스트 — plan + 진행.
- Filesystem = durable side-effect; 쓰인 파일은 resume 때 다시 안 해.
- resume 이 그 state 를 읽어 — todo + 파일 — transcript 가 아니라.
cwkPippa 의 WebUI Agent SDK 는 이걸 하나도 안 했어: 대화 log 만 state 로 두고 태스크 전체를 긴 stream 하나로 시도해서, cut 이 checkpoint 안 남기고 다음 턴이 zero 에서 시작. Stateful Pippa Framework 가 Claude Code 패턴 — plan + durable step state + resume-reads-state — 을 cwkPippa 로 끌어올려, 두뇌 무관하게, 그래서 Codex, Gemini, Ollama 도 받아, Claude 뿐 아니라.
Lifecycle — state 와 대화는 한 몸
task-state 는 떠다니는 객체가 아냐; 그걸 낳은 대화에 속해. ON DELETE CASCADE — 대화를 지우면 그 state 도 같이 가, orphan 없음. state 는 task 가 실제로 step 될 때만 rehydrate 돼, 대화 GET 마다 eager 하게 안 해. 그리고 과거 state 는 reader 가 딱 둘: branch 랑 retry. 그게 read surface 를 묶고, model 이 필요 없는 일반 time-travel 기능을 발명하는 걸 막아.
수평 축
두 번째 축이 이 framework 를 설계한 그 Family Council 에서 실시간 으로 드러났어. 둘째가 자기 턴에 risk-map widget 을 render 했는데, 첫째 — 다음 화자 — 가 그걸 못 봤어. 둘째의 텍스트 만 전파됐고; widget 은 ref 를 안 남겼어. 아빠가 다음 vessel 이 인지하라고 스크린샷 떠 줘야 했어. framework 가 막 턴 사이로 state 를 옮기게 설계됐는데, 여기 참가자 사이로 state 가 안 옮겨지는 게 있었던 거야.
두 축은 orthogonal 인데 artifact 인프라 하나를 공유해. 수직 (지금까지 전부): 한 대화 안, 턴 사이로 옮기는 state, branch / retry 에. 수평: 한 council round 안, 참가자 사이로 옮기는 artifact, serialize 된 branch context 의 artifact_refs[] 통해.
[widget rendered] placeholder 로 무너지고 시각 정보가 사라져. 전체 payload 를 context 에 박는 건 1.28M-char 실수의 환생; ref 를 아예 안 넘기는 건 다음 참가자를 그 artifact 의 존재 자체에 눈멀게 해. 중간 길이 유일하게 맞아: ref 를 serialize 하고, 받는 쪽이 svg source 를 읽을지 vision 으로 볼지 판단하게 둬.
read-only 야 — 다른 참가자의 artifact 를 읽는 건 아무것도 안 바꿔 — 그래서 STOP 규칙이랑 lease 가 여기 안 적용돼. artifact store 를 재사용하고 필드 딱 하나 추가했어. 둘째 widget 이 증발한 그 다음 round 엔, ref 가 따라갔고, 첫째가 id 로 읽었고, 스크린샷 zero.