2주차 컨텍스트 엔지니어링 + 3주차 루프 #52
Conversation
레포에 박혀 있던 절대경로(/Users/joy/...)를 걷어내고, 설치 시점에 현재 clone 경로와 로그인 사용자명으로 자동 구성하도록 변경. - launchd/install.sh 추가: plist를 heredoc으로 ~/Library/LaunchAgents에 생성·로드 + remind-cron crontab 멱등 등록. 기존 심링크를 rm -f로 끊고 실제 파일을 써서, 심링크를 통해 레포 plist가 되살아나는 것을 방지. - launchd/uninstall.sh 추가: 데몬 언로드 + plist 삭제 + crontab 정리. - com.joy.telegram-listener.plist 삭제: 절대경로가 커밋에 남지 않도록 install.sh 생성으로 대체. - restart-listener-on-change.sh: LABEL·LOG를 id -un / $(dirname) 기반으로 동적 계산 (하드코딩 제거, install.sh가 만든 Label과 일치). - README: "세션 밖 자동화 설치" 절 추가, 디렉터리 구조 갱신. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
cache.sh(신규): notion.sh read(curl 동기 ~0.5s)를 매번 타던 조회를 로컬 read-model로 뺀다. stale-while-revalidate — 캐시가 신선하면 ~10ms 즉시 반환, 오래되면 백그라운드 갱신. read-model = 순수 Notion 스냅샷 + 오버레이 2장(읽기 시점 _emit 한 곳에서 병합): - outbox 오버레이: 아직 동기 안 된 "생성"(방금 캡처) 항목을 스냅샷 위에 얹어 즉시 노출. - pending-done 오버레이: 아직 Notion 확인 안 된 "완료"를 낙관적으로 done 표시. 스냅샷은 순수 유지 → 백그라운드 refresh와 done이 서로 덮어쓰는 레이스 원천 차단. list-view.sh: 항목 조회를 notion.sh 직접 호출 → cache.sh read로 전환. 상태 필터는 전체 반환 후 jq 단에서 거른다. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
텔레그램 /capture: 기본 케이스의 claude -p 콜드스타트(실측 10.5s)를 우회하고 결정론 bash 경로로 처리(~0.03s). telegram-listener에 /capture) 케이스 추가(/list와 동일 패턴). 보안 3종(chat_id·명령 화이트리스트·eval 미사용) 유지 — 인자로만 전달. - classify.sh(신규): 사전 기반 카테고리 분류(모델 0). bash 3.2 호환, "가장 긴 매칭 키워드 우선" 규칙으로 부분매칭 모호성(약속 vs 약) 완화. - capture.sh(신규): 입력→분류→저장→노티스 결정론 엔트리. - capture-fast.sh(신규): 동기 Notion write(~0.48s) 대신 로컬 outbox 즉시 저장 + detached 백그라운드 동기. 실패 시 outbox 잔류 → capture-flush.sh(신규) 재시도(유실 방지). - capture/SKILL.md: 분류 서브에이전트 제거, 저장 비동기화, 두 실행 경로(대화형 메인 인라인 분류 vs 텔레그램 classify.sh 사전 분류, 저장은 capture-fast.sh 공유) 문서화. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
done-fast.sh(신규): 완료 처리를 read-model 위에서 낙관적으로 즉시 반영한다. cache.sh mark-done으로 pending-done 오버레이에 id를 넣어 /list·/done에 곧바로 done으로 보이게 하고, Notion status 업데이트는 백그라운드로 던진다. → 네트워크 대기 없이 완료 UX. done/SKILL.md: 위 흐름에 맞춰 절차 갱신. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
tests/smoke_crud.sh: capture→list→plan→done 흐름의 로컬 스모크 테스트. tests/coverage.sh: 스킬·훅·데이터 파일 대비 테스트 커버리지 점검. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
sync-readme 스캔 결과 실제 존재하는 _shared/state-sync-writer.md가 문서에서 누락돼 있어, 에이전트 종류 표·디렉터리 트리·공유 설명에 등록. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
캐시가 없는 cold 경우에만 타던 동기 네트워크(~0.68s)를 SessionStart 훅의 프리워밍으로 제거하는 방식과, 모델 추론 턴이라는 물리적 바닥으로 end-to-end 1초 보장이 불가능하다는 한계를 함께 문서화. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
tasks.json·clarified-specs.json·cache/·outbox/ 은 사용자 개인 할일 데이터와 로컬 캐시라 커밋 대상이 아님. secret·로그류를 개별 무시하던 기존 규칙과 같은 맥락으로 무시 처리. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
같은 지식이 스킬·서브에이전트·셸 스크립트에 복제돼 하나 바꾸면 전부
손봐야 하던 문제를 정본(Single Source of Truth)으로 해소한다.
- .claude/context 신설 ("읽히는 지식" 전용):
data-model / categories / status-lifecycle / design-principles / security
- 연결 방식 A(스킬별 참조): 각 SKILL.md·서브에이전트 상단에 참조 블록 추가
→ 해당 스킬 실행 시에만 로드, 평상시 토큰 비용 없음
- capture SKILL의 잘못된 "분류 단일 출처" 서술을 categories.md로 정정
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
"안 읽으면 사고"가 나는 안전 정본만 무조건 보장이 필요하므로, Lazy(스킬별 Read)에서 Eager(상시 주입)로 승격한다. 나머지 4개 정본은 Lazy 유지(하이브리드). - CLAUDE.md: @.claude/context/security.md 상시 주입 + "상시 주입 정본" 섹션 - remind/SKILL.md: security를 Lazy Read 목록에서 제외, 상시 주입됨을 명시(중복 Read 방지) - context/README.md: 연결 방식을 하이브리드로 갱신(로딩 컬럼 + 승격/강등 기준) 검증: 새 헤드리스 세션에서 security 불변 규칙은 컨텍스트에 로드됨(본문 그대로 인용), status-lifecycle 본문은 미로드 — Eager/Lazy 대조 확인 완료. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
SKILL.md는 매 실행 컨텍스트에 로드되므로 실행에 필요한 지시만 남기고, 속도 최적화의 "왜"(조회 캐시화·완료 크리티컬 패스 제거 등 배경 설명)는 런타임 미로드 문서 NOTES.md로 뺐다. 참조 정본도 로직 수정·디버깅 때만 Read. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- "언제 무엇이 컨텍스트 창에 들어오나"를 시간축(시퀀스)·순간별 스냅샷으로 도식화 - Eager(security.md)/Lazy(4종)를 시점·주체·상주기간으로 대비 - §2에 스킬·에이전트별 참조 정본 표 + grep 재검증 팁 수록 - 주입 한 주제에 집중 (폴더 성격·지식그래프·갱신규칙은 README 소관) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
정본 컨텍스트가 실제 "주입 배선"에 걸려 있고 문서와 현실이 일치하는지 결정적으로 검증한다. lint.sh(파일 무결성)가 못 보는 배선 무결성을 커밋 전에 잡는다. - 검사 5종: Eager @import 존재·비어있지 않음(0바이트 빈 주입 포함), 고아 정본, context-map 소비자표 ↔ 실제 참조 양방향 드리프트, README Eager 표기 정합성 - 소비자는 하드코딩 대신 실제 파일 존재로 동적 해석 → 표에 가짜 행을 넣어 검증을 우회하는 것을 차단하고, 해석 불가한 이름은 건너뛰지 않고 실패시킨다 - 표 파서가 깨지면(헤더/열 변경) 무더기 오탐 대신 "파서 못 찾음/열 불일치"로 명확히 실패 - run-tests.sh L1 게이트에 편입(CI l1+l2 자동 포함), tests/README에 계층·알려진 한계 문서화 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
sync-readme가 README 전문(13.7KB)과 디렉터리 트리를 메인 컨텍스트에서 직접 Read하던 것을, 읽기 전용 공유 서브에이전트 state-scanner에 위임한다. 스캐너가 자기 컨텍스트에서 스캔·비교를 흡수하고, 메인에는 압축된 "사실 + 불일치" 두 블록만 반환한다. - 신규 _shared/state-scanner.md: writer와 대칭인 수집 담당 공유 서브에이전트. 입력 계약(스캔 지시·비교 대상·도메인 관심사) + 불변 규칙(읽기 전용, 원자료 통째 반환 금지, 비밀값 존재만). "반환 텍스트가 곧 산출물" 명시로 빈 응답(스텁) 방지. - sync-readme Step1·2(스캔+README Read)를 스캐너 위임으로 교체 → 메인 흡수 ~5,108토큰 → ~513토큰(89%↓). writer가 어차피 자기 컨텍스트에서 README를 다시 읽으므로 메인이 중계할 필요가 없다는 게 핵심. - context-map 소비자표에 state-scanner 행 추가(정본 참조 design-principles 1종). 측정 근거: OLD=스캔4177B+README13703B, NEW=스캐너 압축 반환 1797B. sync-test는 grep-only(스캔 271토큰)라 위임 시 서브에이전트 비용이 이득을 초과 → 의도적으로 미적용(위임 손익분기: 메인 절감 × 잔존시간 vs 서브에이전트 컨텍스트 비용). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Eager 정본(CLAUDE.md + @import 대상)은 스킬 실행과 무관하게 매 세션 항상 컨텍스트 창에 상주하므로, 여기가 커지면 모든 세션이 그 비용을 영구히 부담한다. "정말 모든 상황에서 필요한가"를 통과 못 한 내용이 슬그머니 Eager로 승격되는 회귀를 L1에서 잡는다. - @import 목록을 하드코딩하지 않고 CLAUDE.md에서 동적 파싱 → 새 Eager import 자동 포함 - 주 게이트=근사 토큰(EAGER_MAX_TOKENS 기본 1200), 보조=줄 수(EAGER_MAX_LINES 기본 80) - 초과 시 상한 조정이 아니라 "Lazy로 내릴 내용 재심사"를 유도하는 실패 메시지 - run-tests.sh L1에 편입(lint+inject+budget), tests/README에 inject와의 역할 분담 문서화 - inject.sh(배선 존재)와 상보: budget은 그 배선의 페이로드 크기를 본다 현재 Eager 50줄·783토큰(상한 내). 음성테스트(상한 100→exit 1)로 게이트 실동작 확인. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
sync-readme가 scanner→writer 두 서브에이전트를 거치던 것을, 스캔·비교·갱신을 한 창에서 끝내는 readme-sync-agent 한 곳에 위임하도록 통합한다. 서브에이전트를 두 번 열면 메인은 가벼워져도 총 토큰이 오히려 늘고(직전: scanner~42k + writer~40k ≈ 82k), README 갱신은 "스캔한 사실을 그대로 옮기는" 단순 전사라 수집·작성을 나눌 실익이 없었다. - 신규 _shared/readme-sync-agent.md: 스캔+대상 Read+최소 diff 갱신+요약 반환을 자기 창에서 전부 수행. 원자료를 메인으로 중계하지 않아 총 토큰 ~42k, 메인 흡수 수십 토큰. - _shared/state-scanner.md 삭제(통합으로 대체). state-sync-writer는 유지 — 사람이 중간에 "자동 채움/보고만"을 결정하는 sync-test가 여전히 수집·작성 분리를 필요로 하기 때문. - context-map §1-3/§1-4/§2를 readme-sync-agent로 정정. §1-4에 "위임은 서브에이전트 창을 통째로 여는 비용이 있다"는 한계를 명시(89%↓만 강조하던 서술 교정) — 메인이 큰 파일을 Read할 때만, 또 창을 한 번만 열 때만 이득임을 남긴다. - state-sync-writer/sync-test 문서의 "sync-readme도 재사용" 서술 정정. 검증: L1(lint·inject·budget) + L2 = 33+9+2+13 pass / 0 fail. state-scanner 잔존 참조 0건. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
inject.sh(배선 존재)와 상보적으로, 정본 주입이 스킬 "동작"을 실제로 바꾸는지를 통제 실험으로 측정한다. 분류 정본 categories.md 주입 有無(Arm A/B)를 같은 모델· 같은 입력 12종·각 3회(72관측)로 대조. - tests/ab-injection/: 정답셋(ground-truth.tsv)·원자료(raw-runs.tsv)·재현 채점기 (score.sh)·설계/결과 문서(README.md). 채점은 손계산이 아닌 TSV 재계산이라 검증 가능. 결과: 정확도 50.0%→97.2%, 6종 어휘 준수 50.0%→100%, 3회 일관성 58.3%→91.7%, 함정 항목 22.2%→88.9%. 미주입 arm은 스키마 밖 라벨 8종을 지어내 list-view 그룹핑을 깸. - tests/README.md: ab-injection을 "게이트 아닌 1회성 효과 측정"으로 인덱스에 연결 (inject=배선 존재 / ab-injection=효과, 상보 관계 명시). - docs/mission-2-report.md: 미션 5개 과제(필수 3+도전 2) 과제별 판정·근거 리포트. GitHub 웹 렌더링용 마크다운(유니코드 막대로 A/B 시각화, mermaid 미지원 환경도 무해). 검증: L1(lint 35 + inject 9 + budget 2) pass / 0 fail. 리포트 상대링크 7개 실재 확인. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
자기 관찰 루프의 write 절반을 배선한다. 그동안 skill-invocations.log는 security.md 정본에 등록만 돼 있고 실제로 기록하는 코드가 없어 6/25 이후 멈춰 있던 고아 로그였다. PostToolUse(matcher=Skill) 훅으로 스킬 실행 시마다 "시각 | #N | 스킬명" 한 줄을 append 해 로그를 되살린다. 동시에 구조적 틈을 메운다: 훅 스크립트는 커밋돼 있었지만 이를 활성화하는 배선(hooks 설정)은 gitignore된 settings.local.json 에만 있어 clone 시 재현되지 않았다. 공유 hooks 블록을 tracked settings.json 으로 승격하고, 개인 permissions 만 settings.local.json 에 남긴다(Claude Code가 둘을 병합). 이제 OS 배선이 repo 안에서 재현된다 — CLAUDE.md의 "OS 파일은 프로젝트 안에" 원칙에 부합. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
매 커밋 직전에 결정적 테스트(run-tests.sh l1 l2, ~1초)를 돌려, 정본이 실제 상태와 어긋난 채 커밋되는 것을 구조적으로 막는다. L1 이 끊어진 참조 링크 (lint) · 정본 주입 배선 끊김/고아 정본/지도 드리프트(inject) · Eager 상주 정본 크기 회귀(context-budget)를 잡고, L2 가 detect-todo.js 계약을 검증한다. 자격증명·LLM 이 필요한 L3(smoke)는 게이트에서 제외 — 느리고 비결정적이라 매 커밋 게이트엔 부적합. 재현성 — .git/hooks 는 버전 관리가 안 돼 클론 시 훅이 사라진다. 훅을 추적되는 .claude/githooks 에 두고 git 의 core.hooksPath 로 연결한다. 클론한 사람은 'bash .claude/githooks/install.sh' 한 번이면 게이트가 산다(uninstall.sh 로 해제). settings.json 승격과 같은 "배선을 repo 안에서 재현" 원칙. 검증 완료: 고아 정본을 만들어 커밋 시도 → L1 FAIL 로 차단, HEAD 불변 확인. 긴급 우회는 git 네이티브 --no-verify 로 가능. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
log-skill-invocation.sh(write 절반)가 쌓은 skill-invocations.log 를 읽어 사용 패턴을 뽑는다. 이로써 "관찰 → 개선" 루프가 닫힌다. usage-report.sh(결정론적 공유 스크립트)가 세 가지를 뽑는다: ① 빈도 — 자주 쓰는 스킬 순위 (별칭·고속화 후보) ② 연쇄 — 바로 이어 부른 쌍(capture→plan 등), 임계 이상 반복 시 콤보 제안 ③ 유휴 — 등록됐지만 호출 이력 없는 스킬 (폐기·점검 후보) /usage 스킬은 이 스크립트를 1회 실행하고 출력을 verbatim relay 한다 (/skills·/remind-when 과 같은 "정본 직독 + 결정론 조회" 패턴). 정직한 한계: 로그에 성공/실패 필드가 없어 "실패 반복"은 감지 못 한다. 원하면 write 훅이 tool_response 상태까지 남기도록 스키마를 넓혀야 한다(후속). 검증: 실제 로그(2줄)→"표본 얕음" 경고, 풍부한 합성 로그(11줄)→빈도·연쇄· 콤보 제안·유휴 세 섹션 모두 정확 출력 확인(테스트 후 로그 원상복구). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
/sync-readme 로 파일시스템을 스캔해 이번 세션의 추가분을 README에 반영:
- 스킬 표에 /usage 추가
- 에이전트 표에 README Sync 추가, State-Sync Writer 범위를 sync-test 전용으로 정정
- 훅 표에 log-skill-invocation(PostToolUse matcher Skill) 추가
- 디렉터리 트리에 settings.json·skill-invocations.log·githooks/·usage/·
readme-sync-agent.md·usage-report.sh 등록
최소 diff, 기존 한국어 톤 유지.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
/sync-test 로 커버리지 빈틈을 스캔한 결과, 이번 세션에 추가된 순수 결정적 스크립트 두 개가 무테스트였다. telegram-listener·remind-cron 같은 외부 의존 .sh 와 달리 이 둘은 네트워크·데몬·claude 의존이 전혀 없어(stdin→append / 로그→stdout) detect-todo.js 처럼 단위 검증이 가능하다. - tests/unit-scripts.sh 신규: log-skill-invocation(append·카운터 증가·비-Skill 스킵·네임스페이스 보존)과 usage-report(빈도·연쇄·유휴·빈 로그) 계약 검증. - 테스트 격리를 위해 두 스크립트에 SKILL_LOG 환경변수 오버라이드 추가 — 임시 로그를 주입해 실데이터(skill-invocations.log)를 건드리지 않는다. 기본 동작은 불변. - run-tests.sh L2 에 배선(CI L1+L2 가 자동 커버), tests/README.md 문서화. 검증: run-tests.sh l1 l2 통과(L2 13+8 pass), 실로그 무접촉 확인. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
세션 밖에서 도는 자동화 루프 3개를 crontab 으로 배선한다(remind 와 같은 방식). 1) outbox 재동기 (조정 루프 완성) — flush-cron.sh, 15분마다 capture-flush.sh 를 주기 구동해 outbox 미동기 항목을 재전송, "모두 동기됨" 상태로 수렴. 몸통만 있고 자동 구동이 없던 조정 루프의 나머지 절반. 2) 감시 (watchdog) — watchdog-cron.sh, 10분마다 telegram-listener 데몬 헬스 체크. 다운 감지 시 폰 알림 + 자동 재시작 시도. 상태 파일로 healthy↔down 전이에만 알림(디바운스). launchd KeepAlive 가 못 잡는 "언로드/미기동"을 사용자에게 알린다. 3) 집계 (digest) — digest-cron.sh, 매주 일요일 20:00 digest-report.sh(결정론 집계)로 상태 분포·카테고리·2일+ 방치 draft 를 요약해 폰으로 발송. remind 가 매일 재촉이라면 이건 주 1회 회고. 공유·인프라: - _shared/telegram-send.sh: 알림 루프 공용 sender (security.md 규칙 준수 — 값 노출 없이 실패, 자격증명 없으면 미발송). - launchd/install.sh·uninstall.sh: cron 4종 멱등 등록/제거. - .gitignore·security.md 정본: 새 런타임 상태·로그 파일 등록(양쪽 일치). 테스트·검증: - unit-scripts.sh 에 digest-report(집계 로직)·telegram-send(빈 메시지 가드) 결정론 테스트 추가(ITEMS_JSON_FILE 픽스처 주입). run-tests l1 l2 통과(14 pass). - 게이트가 inject.sh 검사4 로 telegram-send 의 미문서화 정본 참조를 잡아내, .sh 는 §2 주입 지도의 소비자가 아니라는 관례에 맞춰 경로 인용을 제거해 해소. - README·tests/README 를 실제 상태에 동기화. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
로컬-우선(§2)이 데이터를 여러 로컬 파일에 쌓지만 "로컬에 쌓인다"가 같다고 캐시·버퍼·축적을 뭉뚱그리면 안 된다 — 정상 상태가 정반대다. 저장소를 만지기 전에 셋 중 무엇인지 목표 상태로 판정하는 기준을 정본화한다. - 캐시(원본과 일치·재생성 무해) / 버퍼(0이 목표·지우면 유실) / 축적(끝없이 성장) 을 표로 레포 실체에 매핑. - 루프도 같은 축으로 분리: 자율탐지형(조정 — flush·watchdog·digest) vs 자산 축적형(자기 관찰 루프 = 로그 write ↔ /usage read). - 혼동 방지: capture 로컬 저장부는 캐시+버퍼이지 축적이 아니며, outbox 는 데이터일 뿐 루프는 그걸 드레인하는 flush-cron(탐지형)임을 명시. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- context-map.md 를 .claude/context/ → docs/ 로 이동하고 README 링크 보정 - mission-2-report.md 의 context-map 링크를 새 위치 기준으로 보정 - system-loops.md 신규: 세션 밖 시스템 루프 6종(cron 4·데몬·자기관찰)의 동작 순서를 탐지형/축적형 축으로 정리한 mermaid 다이어그램 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
context-map.md 를 docs/ 로 옮기면서 §4 드리프트 검사가 파일을 못 찾아 조용히 SKIP 되던 것을 되살린다. 검사 8→9 pass 로 복귀. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
2,3주차 과제 잘 보았습니다! 또한, 구조를 잡으며 긍정적인 부분을 보고 계신 것 같아 강사로서 기쁘네요. 랄프 루프로 성과도 내셨다니 보기 좋습니다.
바쁘신 와중에 세 번의 과제를 진행하시느라 고생 많으셨습니다. 회사에서는 여러 복잡한 이슈로 AI의 잠재력을 100% 활용하기 어려울텐데요, 개인 OS의 장점은 100% 이상의 잠재력을 제한 없이 이끌어낼 수 있다는 점인 것 같습니다. 본인의 필요에 의한 OS인 만큼 내 필요를 더 탁월하게 채워주는 OS로 계속 발전하면 좋겠습니다. 고맙습니다! |
민창님 안녕하세요!
2주차와 3주차 과제 함께 리뷰 요청 드립니다
1주차와 달리 좀 많이 헤맸지만..! 덕분에 단순 채팅형을 벗어나서 좀 더 똑똑하게 ai 사용 과정을 설계할 수 있게 되어 기쁩니다!
skill 이 느려서 고민이었는데 랄프 루프로 속도 개선이 많이 되었습니다!
1. 컨텍스트 엔지니어링
1.1 고민한 사항 : 도메인 컨텍스트 주입 고민 -> 사용자마다 다양해질것 같아 일단 보류
1.2 본 과제에서 적용한 사항: 스킬에서 반복적으로 시간을 잡아먹는 부분을 컨텍스트로 분리
2. 루프
2.1 추후 추가로 구현할 루프
시스템 루프는 "반복할수록 시스템을 견고하게 만들어준다"라는 부분이 인상적이었습니다!
2.2 본 과제 내 루프 구현사항
단순루프
탐지형 루프: notion 동기화 루프 , 텔레그램 리스너 health 체크,
축적형 루프 - 스킬 로그 분석하여 OS 개발시 더 나은 방향 제안
3. 루프 다이어그램
세션 밖에서 스스로 도는 루프들의 동작 순서. 이 OS의 자동화는 설계 원칙
§7의 축으로 갈린다.노드 색상 범례 — 🔵 트리거/진입 · 🟢 정상·성공 · ⚪ 무소음·종료 · 🔴 경보·실패 · 🟡 축적 저장소
A · 탐지형(조정) 루프 — crontab 4종
주기 감지 → 반응 → 잊기. 버퍼를 0으로 드레인하거나 데몬을 지킨다. 평상시엔 로그도
알림도 없이 조용하고, 이상이 잡힐 때만 소리를 낸다.
flush-cron— outbox 재동기 · 15분마다capture가 백그라운드로 던진 Notion 반영이 실패해 outbox에 남으면, 이 루프가 재전송해
"모두 동기" 상태로 수렴시킨다.
flowchart TD T(["cron · 15분마다"]):::trig --> R["capture-flush.sh 실행"] R --> Q{"outbox 비었나?"} Q -->|비었음| Z(["즉시 종료 · 무소음"]):::quiet Q -->|잔여 있음| S["Notion 재전송 시도"] S --> C{"flushed 있음?"} C -->|예| L["flush-cron.log 기록 (건수만)"]:::ok C -->|아니오| Z L --> Z classDef trig fill:#dbeafe,stroke:#3b82f6,color:#1e3a5f; classDef quiet fill:#eef2f6,stroke:#94a3b8,color:#475569; classDef ok fill:#dcfce7,stroke:#22c55e,color:#14532d;watchdog-cron— 데몬 감시 · 10분마다telegram-listener가 언로드·비정상이면 KeepAlive만으론 조용히 죽는다. 다운을 감지해
폰에 알리고 자동 재시작한다(상태 전이에만 알림 — 디바운스).
flowchart TD T(["cron · 10분마다"]):::trig --> H{"state = running?"} H -->|healthy| RC{"직전이 down?"} RC -->|예 · 복구| N1["복구 통지 발송"]:::ok RC -->|아니오| SV["state 저장"]:::quiet N1 --> SV H -->|down| DB{"down 신규 전이?"} DB -->|예| N2["다운 경보 · 디바운스"]:::alert DB -->|아니오| RS["kickstart / bootstrap 재시작"] N2 --> RS RS --> OK{"재시작 성공?"} OK -->|예| SV OK -->|아니오| LG["로그 · 다음 주기 재시도"]:::alert LG --> SV classDef trig fill:#dbeafe,stroke:#3b82f6,color:#1e3a5f; classDef quiet fill:#eef2f6,stroke:#94a3b8,color:#475569; classDef ok fill:#dcfce7,stroke:#22c55e,color:#14532d; classDef alert fill:#fee2e2,stroke:#ef4444,color:#7f1d1d;digest-cron— 주간 집계 · 매주 일 20:00할일 현황(상태 분포·카테고리·방치 draft)을 주 1회 요약해 폰으로 보낸다.
집계(결정론)와 발송(부작용)을 분리한다.
flowchart TD T(["cron · 매주 일 20:00"]):::trig --> A["digest-report.sh 집계"] A --> B{"요약 텍스트 있나?"} B -->|없음| Z(["스킵"]):::quiet B -->|있음| S["telegram-send.sh 발송"] S --> R{"발송 성공?"} R -->|성공| L1["digest-cron.log · 성공"]:::ok R -->|실패| L2["로그 · 스킵 (자격/네트워크)"]:::alert classDef trig fill:#dbeafe,stroke:#3b82f6,color:#1e3a5f; classDef quiet fill:#eef2f6,stroke:#94a3b8,color:#475569; classDef ok fill:#dcfce7,stroke:#22c55e,color:#14532d; classDef alert fill:#fee2e2,stroke:#ef4444,color:#7f1d1d;remind-cron— draft 재촉 · 매일 17:00미처리 draft가 남아 있으면 매일 저녁 폰으로 리마인더를 쏜다. 세션 안 스킬(
/remind)을세션 밖 cron이 대신 호출한다.
flowchart TD T(["cron · 매일 17:00"]):::trig --> C["claude -p /remind"] C --> D["draft 항목 조회"] D --> Q{"미처리 draft 있나?"} Q -->|있음| S["telegram 리마인더 알럿"]:::ok Q -->|없음| Z(["조용히 종료"]):::quiet classDef trig fill:#dbeafe,stroke:#3b82f6,color:#1e3a5f; classDef quiet fill:#eef2f6,stroke:#94a3b8,color:#475569; classDef ok fill:#dcfce7,stroke:#22c55e,color:#14532d;B · 상시 데몬 루프 — launchd
cron처럼 깨었다 자는 게 아니라 launchd가 상시 띄워 둔다. getUpdates를 long-poll로 걸어
폰 명령이 오는 즉시 처리하고, 보안 3종(chat_id 화이트리스트 · 명령 화이트리스트 ·
eval 미사용)을 통과한 것만 실행한다.
telegram-listener— 인바운드 long-poll · 상시(KeepAlive)flowchart LR B(["launchd 상시 데몬"]):::trig --> P["getUpdates long-poll · 25초"] P --> M{"메시지 도착?"} M -->|타임아웃| P M -->|도착| W1{"chat_id 화이트리스트?"} W1 -->|불일치| SK["스킵"]:::alert W1 -->|일치| W2{"허용 명령? (/capture /list /plan)"} W2 -->|아니오| RJ["거절 안내"]:::alert W2 -->|예| EX["로컬 스킬 실행 · eval 미사용"]:::ok EX --> RE["응답 발송"] RE --> OF["offset 전진 · 저장"] SK --> OF RJ --> OF OF --> P classDef trig fill:#dbeafe,stroke:#3b82f6,color:#1e3a5f; classDef ok fill:#dcfce7,stroke:#22c55e,color:#14532d; classDef alert fill:#fee2e2,stroke:#ef4444,color:#7f1d1d;C · 축적형 루프 — 자기 관찰
이 레포의 유일한 순수 축적형 루프. 스킬을 부를 때마다 append-only 로그에 한 겹 쌓고(write),
나중에
/usage가 그 축적물을 회수해 사용 패턴을 낸다(read). 저장소는 커질수록 가치가 있어드레인하지 않는다.
self-observation— PostToolUse 훅 →/usageflowchart LR K["Skill 툴 실행"]:::trig --> HK["PostToolUse 훅 (log-skill-invocation)"] HK --> AP["한 줄 append · 시각 · #N · 스킬명"] AP --> LOG[("skill-invocations.log · append-only")]:::store LOG --> RD["/usage 호출"]:::trig RD --> AN["로그 분석"] AN --> OUT["자주쓰는 · 연쇄 · 유휴 스킬 표시"]:::ok classDef trig fill:#dbeafe,stroke:#3b82f6,color:#1e3a5f; classDef ok fill:#dcfce7,stroke:#22c55e,color:#14532d; classDef store fill:#fef3c7,stroke:#f59e0b,color:#78350f;출처 ·
.claude/hooks/{flush,watchdog,digest,remind}-cron.sh·telegram-listener.sh·log-skill-invocation.sh· 스케줄launchd/install.sh· 분류 축context/design-principles.md §7