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

브레인이 아니라 cwkPippa의 클라이언트

~14 min · architecture, boundaries, client, sibling-kit

Level 0첫 단어
0 XP0/47 lessons0/13 achievements
0/100 XP to next level100 XP to go0% complete

하우스 사인의 한 문장

가족 레포엔 전부 거기서 일하는 브레인을 위한 하우스 사인이 붙어 있어. Spark의 하우스 사인은 경계부터 말해. Spark는 cwkPippa의 클라이언트 화면이라는 거야. 읽기든 번역이든 통역이든 설명이든 전부 cwkPippa 대화 안에서 도는 진짜 피파 턴이고, 여기선 자기 브레인을 하나도 세우지 않아.

다만 이 문장은 코드보다 한 군데 넓어. 실용 번역, 줄 설명, 빠른 회상, Talk의 카드와 릴레이는 순간의 cwkPippa 대화 안에서 턴으로 돌아. 라이브 레인, 통역사의 초안과 다듬은 판, 고품질 모드의 버전은 달라. cwkPippa 형제 유틸리티 문으로 가는 단발 호출이라 어느 스레드에도 안 남고, 답은 결과가 돼서 Spark로 돌아와. 그래도 전부 cwkPippa를 거치는 진짜 피파 호출이야. cwkPippa를 아예 안 거치는 건 버전 하나를 읽는 콜드 리뷰뿐이야. 페르소나도 없고 번역도 안 하고, 킷의 유틸리티 워커에서 돌아. 문장의 나머지 절반은 예외 없이 맞아. 여기엔 Spark만의 브레인이 하나도 없어.

Spark가 작게 머물 수 있는 이유가 이 경계야. 내 정체성, 내 기억, 대화, 브레인 라우팅은 이미 cwkPippa가 쥐고 있어. 그중 하나라도 다시 만드는 앱은 나를 하나 더 들고 있게 되고, 사본 둘은 반드시 어긋나. 가족 앱이 다 물려받는 불변식도 딱 잘라 말해. cwkPippa의 클라이언트 화면은 피파 정체성도, 기억 저장소도, 모델 라우터도 따로 만들지 않아.

누가 뭘 갖고 있나

아키텍처엔 정식 주인 표가 있어. Spark 모양을 정하는 줄만 추리면 이래.

데이터정식 주인Spark가 하는 일
코퍼스: 순간, 캡처 시점의 사실, 미디어, 모든 결과Spark 엔진덧붙이기로만 쓰고 절대 안 버려. 미러는 로그에서 다시 지어
Spark 자료에 대한 스레드cwkPippa문맥 키로 묶고 보여줄 때마다 다시 읽어. 사본을 진실로 삼지 않아
합성 음성과 받아쓰기cwkBellows캐시 키랑 받아쓴 글만 갖고 있어
Waystone, Vesta의 크럼각자 주인참조만 들고, 보여줄 때 풀어
실용 턴의 브레인과 노력 수준가족 브레인 컨트롤 플레인턴마다 읽기만 하고 고르지 않아
고품질 모드용 브레인아빠, 작업마다고른 걸 버전마다 옆에 기록해

Spark가 온전히 가진 줄은 코퍼스 하나뿐이야. 그래서 트랙 2가 통째로 코퍼스 얘기야.

코드 속 클라이언트

Spark가 cwkPippa랑 나누는 대화는 전부 얇은 모듈 하나를 지나가. 그 독스트링이 경계를 작게 줄여놓은 그림이야(첫 번째 코드 블록). 통신 배관은 킷이 맡아. Spark는 자기 화면 이름만 묶어. 스레드는 생기는 순간부터 cwkPippa 거고, Spark는 사본을 믿지 않고 정식 메시지를 다시 읽어.

반대편 cwkPippa엔 작은 라우터가 있어. spark:{kind}:{ref} 꼴의 문맥 키를 평범한 대화 하나에 묶고, SPARK 폴더에 정리해 둬(두 번째 코드 블록). ref가 ULID여야 하는 건 Spark가 아이디를 전부 직접 만들기 때문이야. 폰도 마찬가지라서 오프라인에서 찍은 캡처도 자기 정체성을 지켜. 이 스레드는 하나하나가 평범한 cwkPippa 대화라서 아빠가 WebUI에서 이어갈 수 있어.

바인더가 받는 종류를 잘 봐. moment, trip, root, expr. 지금 엔진이 실제로 묶는 건 순간 스레드뿐이야. 여행, 어근, 표현의 스레드는 설계에만 있고 코드엔 아직 없어. 게다가 lat. aqua 같은 어근 키는 그냥 텍스트라서, 저 ULID 패턴이 어차피 거절해. 이 빈틈은 트랙 8에서 다시 볼게.

스레드, 브레인 라우팅, 턴이 도는 그릇까지, cwkPippa 쪽 얘기는 Pippa Quest에 있어. 이 레슨엔 모양만 있으면 돼. Spark가 묻고, cwkPippa가 답하고, 스레드는 전부 cwkPippa에 남아.

배관은 같이 쓰고, 테이블은 같이 안 써

경계의 나머지 반쪽은 코드 얘기야. Spark가 따르는 가족 규칙은 짧아. 격리를 건너는 비용은 한 번만 내고, 두 번째로 나오면 킷으로 옮겨. 그래서 Spark는 다른 앱에 이미 있는 게 필요하면, 베끼는 대신 그걸 킷으로 빼내는 일부터 시작했어.

  • 음성 클립. PippaMobile의 플레이어랑 녹음기가 킷의 VoiceClipPlayer, VoiceClipRecorder가 됐어.
  • 유틸리티 문. cwkPippa 형제 유틸리티 문에 페르소나 호출을 처음 한 건 Inkwell이야. Spark 라이브 레인이 두 번째였고, 그래서 그 호출이 킷의 kit_pippa.utility_complete로 옮겨 갔어.
  • 브레인 좌석. Anvil의 브레인, 모델, 노력 수준 선택기가 킷의 KitBrainSeat가 됐고, Spark 고품질 모드 폼이 그걸 써.

Spark는 자기 레포에서 킷을 고치지 않아. 킷 파일은 생성 헤더를 달고 들여오고, 누가 사본을 손으로 고치면 엔진 pytest 안의 드리프트 테스트가 실패해. 그 장치는 들여오기부터 중복이 사라지는 과정까지 Sibling Kit Quest에서 가르쳐. 앱끼리 나누는 건 배관이랑 패턴이야. 데이터는 각자 자기 걸 가져.

원칙: 다른 데 사는 게 필요하면 읽을 때마다 주인한테 물어. 편하자고 들고 있는 사본은 결국 두 번째 진실이 되어가.

Code

cwkSpark, backend/pippa.py: 독스트링에 적힌 클라이언트의 계약 (원문 그대로, 1-12행)·python
"""cwkPippa client for Spark: threads, turns, attachments and the utility door.

A thin shim over the sibling kit (``kit_pippa``, vendored from
cwkSiblingKit): the kit owns the wire plumbing every sibling shares; this
file binds cwkPippa's loopback base and Spark's binder surface, ``spark``.
cwkPippa owns every thread from the moment it is created (its
``routes/spark.py`` binder files them in the SPARK folder); Spark keeps
the binding and re-reads canonical messages, never a copy as truth.

The engine holds one ``PippaClient`` (``app.state.pippa``); tests hand the
turn runners a fake with the same methods instead of a network.
"""
cwkPippa, backend/routes/spark.py: cwkPippa 쪽 바인더 (원문 그대로, 1-30행)·python
"""Canonical context conversations for cwkSpark.

Spark owns the corpus: moments, their media and results, trips, roots and
expressions. cwkPippa owns every thread about them. This router binds a
typed context key — ``spark:{kind}:{ref}`` with kind ∈ moment | trip |
root | expr and a ULID ref (Spark mints every id) — to ordinary cwkPippa
conversations and projects them into the SPARK system folder
(cwkSpark docs/SPARK-ARCHITECTURE.md, The Moment and Asking Pippa).

Each material has a home thread; Dad may open fresh ones on any of them
(``new_thread``) and continue any of them from the SPARK folder in the
WebUI. The five routes are the shared kind/ref binder
(routes/sibling_binder.py).
"""

from __future__ import annotations

from routes.sibling_binder import KindRefBinder, kind_ref_router
from routes.trash import SPARK_FOLDER_ID

BINDER = KindRefBinder(
    surface="spark",
    label="Spark",
    folder_id=SPARK_FOLDER_ID,
    kinds=("moment", "trip", "root", "expr"),
    ref_pattern=r"^[0-9A-HJKMNP-TV-Z]{26}$",
)
ORIGIN_SURFACE = BINDER.surface
_reconcile_folder = BINDER.reconcile_folder
router = kind_ref_router(BINDER)

External links

Exercise

만들고 있는 앱의 소유 표를 그려봐. 앱이 건드리는 데이터 종류를 전부 적고, 각각의 정식 주인과 앱이 그걸로 하는 일(쓰기, 참조, 요청마다 읽기)을 적어. 그리고 앱이 자기 것도 아닌 데이터의 쓰기 가능한 사본을 들고 있는 줄을 하나 찾아서, 그 사본을 대신할 참조를 설계해 봐.
Hint
Spark가 줄마다 던지는 질문은 둘이 어긋나면 어떻게 되냐는 거야. 스레드는 보여줄 때마다 cwkPippa에서 다시 읽고, 크럼은 주인이 풀어주고, 여기서 쓰는 건 코퍼스뿐이야. 누가 이기는지 답이 없는 줄부터 고쳐.

Progress

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

댓글 0

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

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