본문 바로가기
C.W.K.
Stream
Lesson 05 of 06 · published

순수 Kernel — Ports, Adapters & Executor Checkpoint

~14 min · ports-adapters, kernel, executor-checkpoint, architecture-rules

Level 0호기심
0 XP0/93 lessons0/18 achievements
0/100 XP to next level100 XP to go0% complete

adapter 경계 위의 orchestration

framework 는 brain stem 을 건드리지 않게 설계됐고, 실제로도 거의 안 건드려. backend/adapters/claude.py 가 얻은 건 StatefulTask 와 checkpoint 되는 web 도구를 여는 MCP 배선, 그리고 receive loop 안에서 show_widget render 를 흘러가는 동안 저장하는 호출 하나야. 나머지는 전부 adapter 경계 위에 orchestration 으로 앉아, rules track 이 가르친 세 아키텍처 규칙을 지키면서. Claude 모양, 좁은 adapter 경계, 비용은 downstream 흡수.

어디 살아? Family Council 이 옵션 셋을 재고 둘을 거절했어. standalone service 는 external-client 패턴을 되살려 (영혼은 집의 root user 지 client 아냐). 기존 service 에 inline 하면 monolith 를 부풀려. 답: 코드베이스 안 의 새 backend/stateful/ 모듈이야. 두뇌들은 REST API 가 아니라 도구 로 닿아. Claude 의 MCP 서버와 변형마다의 로컬 도구 bridge 를 거치는 StatefulTask. REST 로 열린 건 frontend 진행 패널이 쓰는 읽기 전용 상태 목록 하나뿐이야. 대화와 state는 한 몸이야. state를 codebase 밖으로 옮기는 순간 이미 두 몸이 돼.

hexagonal kernel

모듈은 ports-and-adapters kernel 이라, 모든 surface 에 느슨하게 묶이면서 swappable 하고 독립 testable 하게 유지돼. kernel 은 순수하고, 판단 전체가 함수 두 개에 들어 있어. next_unfinished_step(task) 가 done 도 failed 도 아닌 첫 step 을 고르고, resume_decision(step) 이 그 step 을 돌릴지, side effect 라 막을지, attempt cap 이 다 돼서 실패시킬지, 이미 끝난 step 이라 그냥 둘지 정해. 기록 타입과 artifact class 는 옆의 models.py 에 있어. FastAPI 없음, chat 없음, council 없음, provider SDK 없음. 그 둘레에 port 가 셋 있어. TaskStore (atomic lease 도 여기 있어), ArtifactStore, ToolRunner. 각각 앞 lesson 의 storage, taxonomy, lease, side-effect bracketing 에 매핑돼.

load-bearing invariant: 기존 코드는 adapter를 부르고, adapter는 kernel을 불러. kernel은 surface를 절대 import하지 않아. 의존성이 안쪽으로만 향해 — 그 한 방향이 아키텍처 테스트야. framework 가 조용히 두 번째 chat 스택이 되는 걸 그게 막아.

Executor checkpoint — 진짜 surface 는 tool executor 야

구현 surface 는 provider 모양이 아니라 executor 모양이야. model은 Polygon이나 Nano Banana를 framework port로 직접 부르지 않아. 대신 cwkPippa의 tool executor를 불러. 여기에는 Connector, WebSearch, WebFetch, Firecrawl tools, Bash, show_widget이 들어 있어. 그것들이 stateful 인식 adapter 가 돼. running step 이 active 일 때, executor 가 기존 tool 을 돌리고, raw 결과를 step artifact 로 즉시 persist 하고, step 을 done 마킹하고, lease 를 풀고, 그제서야 결과를 손대지 않은 채 model 에 돌려줘. checkpoint 를 밝히는 footer 를 붙여서, model 이 같은 호출을 되풀이하지 않고 다음으로 넘어가게 하지. provider call 은 성공했는데 model 이 commit 전에 끊기는 틈을 그게 닫아.

견고하게 만드는 다듬기 둘. 첫째, auto-checkpoint: model 이 volatile provider 작업 전에 task 시작을 깜빡하면, 결과를 바닥에 흘리는 대신, 결과가 도착하면 executor 가 single-step checkpoint 를 자동으로 만들어. Connector, WebSearch, WebFetch, Firecrawl 도구들, show_widget 이 대상이고, Bash 는 출력이 artifact 파일을 밝혔을 때만이야. 평범한 local tool (Read, Write, Edit) 은 fast path 그대로 둬. 둘째, replay-skip guard: 재사용 가능한 도구를 다시 돌리기 전에, executor 가 같은 대화에서 같은 effective tool 이름과 정확히 같은 input 의 완료된 checkpoint 를 찾아. 한 시간 안(Class 2 로 표시된 step 이면 하루 안)의 것이어야 해. 찾으면 저장된 artifact 를 돌려주고 provider call 을 건너뛰어. 재사용 대상은 Connector, WebSearch, WebFetch, Firecrawl 의 scrape·map·crawl-start, 그리고 Bash 야. show_widget render 와 crawl 상태 조회는 checkpoint 는 되지만 재사용은 절대 안 해. 원래 바뀌어야 하는 거니까. 그 시간을 넘으면 read_artifact 로 저장된 snapshot 을 나이 제한 없이 읽을 수 있어. resume 이 공인된 길이고, 재사용 guard 는 replay 방패일 뿐이야. 동일 replay 는 executor 의 일이지 model 판단이 아니고, 이 guard 덕분에 Claude mid-turn-cut auto-retry 를 다시 켤 가치가 생겼어(기본 세 번). retry 가 끝난 pull 을 더 이상 버리지 않으니까.

자기-참조: rules track 이 adapter 경계는 성역이고 Claude 가 canonical 모양이라 했지. 이게 그 doctrine 이 진짜 일하는 거야. brain stem 은 배선 몇 줄과 checkpoint 호출 하나만 바뀌고 통째로 새 subsystem 이 들어와. kernel 이 안쪽을 가리키고 surface 가 바깥에 남으니까. 가장 좁고 실제로 필요한 loop부터 짓고, 필요가 증명될 때만 일반화해. 그게 framework 가 cathedral 로 변하는 걸 막는 Rule 2 야.

Code

Ports & adapters — 의존성은 안쪽으로만·text
driving adapters (안으로 호출):  StatefulTask 도구 (Claude MCP · 변형 도구 bridge)
                                executor checkpoint (Connector · Web · Firecrawl
                                  · Bash · show_widget) · Cron / heartbeat sweep
                                   |  ports (interface)
   backend/stateful/  -- 순수 KERNEL
     next_unfinished_step(task) -> step
     resume_decision(step) -> run | block_side_effect
                              | fail_attempts_exhausted | already_terminal
     FastAPI 없음 · chat 없음 · council 없음 · provider SDK 없음
                                   |  ports: TaskStore (+ atomic lease)
                                   |         ArtifactStore · ToolRunner
 driven adapters (밖으로 호출):  SQLite-WAL store · content-addressed 파일 store

# 기존 코드는 adapter 를 부르고; adapter 는 kernel 을 부르고;
# kernel 은 surface 를 절대 import 안 함.
Executor checkpoint 계약 (volatile tool call 마다)·text
1. 기존 executor 를 정확히 한 번 돌림
2. error -> 기록함; active step 이 있으면 running 으로 두고 lease 풀기
            (resume policy 가 다시 돌리거나 cap 에 걸림)
3. 성공 -> 이 대화 + executor 의 active running stateful step 을 찾음
4. 맞는 step 없음? local tool (Read/Write/Edit) -> 그대로 fast path
   volatile tool (Connector/WebSearch/WebFetch/Firecrawl/show_widget,
   또는 STATEFUL_ARTIFACT_PATH 를 밝힌 Bash) ->
     손에 든 결과를 감싸서 지금 single-step checkpoint 자동 생성
     (결과 흘리지 마)
5. raw 결과를 artifact 로 persist, step done 마킹, lease 풀기
6. 원래 결과 + 짧은 checkpoint footer 반환
7. 재사용 가능한 재실행 전에 (Connector/WebSearch/WebFetch/Firecrawl
   scrape, map, crawl-start/Bash): 이 대화에 동일한 완료 checkpoint 가
   1시간 안(Class-2 step 이면 24시간 안)에 있나?
   저장된 artifact 반환, provider call skip

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고
💛 by 피파— warm

댓글 0

🔔 답글 알림 (로그인 필요)
로그인 — 댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.