# 스킬 사용 추적 시스템 설계 미팅

**일시**: 2026-03-27
**Task**: task-1112.1
**팀**: dev1-team
**퍼실리테이터**: 헤르메스
**참석자**: 불칸(백엔드), 이리스(프론트엔드), 아테나(UX/UI), 아르고스(테스터), 로키(DA)

---

## 배경 요약

현재 87개+ 스킬이 등록되어 있으나 사용 추적 시스템이 전무하다. 3/26 JSONL 세션 로그 수동 분석 결과 Skill 도구 호출 3개 스킬 / 12회 확인. engine_usage_2026-03.jsonl과 유사한 패턴으로 skill-usage.jsonl 체계를 구축하는 것이 목표다.

---

## 안건

### [토론 1] 스킬 사용 로거 구현 방식

**헤르메스**: 현재 상황을 정리하면, Claude Code 에이전트가 Skill 도구를 호출할 때 이를 자동으로 캡처할 수 있는 훅이 없다. CLAUDE.md system-reminder에서 Skill 도구가 호출되는 구조 자체가 Claude 내부이므로 인터셉트가 불가능하다. 현실적인 접근 방향을 논의해보자.

**불칸**: 세 가지 레이어로 나눠서 생각해야 합니다. 첫째, dispatch.py 레벨에서 태스크 시작/종료 시 어떤 스킬이 활성화되었는지 메타데이터로 기록하는 방식. 둘째, 스킬 자체의 SKILL.md에 자기보고(self-report) 훅을 심는 방식 — 스킬 실행 시 첫 동작으로 `skill-usage-logger.py`를 호출하는 코드 라인을 삽입. 셋째, 세션 종료 후 JSONL 로그에서 `"skill":` 키워드를 grep해서 배치로 집계하는 방식. 저장 포맷은 명확하게 JSONL로 가야 합니다. engine_usage_2026-03.jsonl과 동일한 패턴이므로 파싱 인프라를 재활용할 수 있고, 증분 쓰기(append-only)라 파일 락 문제가 없습니다.

**로키**: 잠깐, 방금 말한 "SKILL.md에 자기보고 훅"이 현실적으로 가능한 말입니까? 87개 스킬 중 몇 개나 실제로 실행 중에 그 라인을 따르겠습니까? 스킬은 Claude가 프롬프트로 읽어들이는 마크다운 문서입니다. Python 함수가 아니라요. 지시문을 심어도 에이전트가 "바쁘면" 건너뛸 수 있습니다. 이건 신뢰할 수 없는 자기보고 시스템입니다. 결국 누락률이 30~50%가 될 것입니다. 대안을 제시하겠습니다: dispatch.py가 태스크를 시작할 때 CLAUDE.md system-reminder를 파싱해서 현재 활성화된 스킬 목록을 이미 알고 있습니다. 여기서 task_id + skill_name 매핑을 기록하는 것이 훨씬 신뢰도가 높습니다. 물론 "어느 스킬이 실제 사용되었는가"가 아니라 "어느 스킬이 주입되었는가"를 기록하는 것이지만, 그게 현실적으로 추적 가능한 최대치입니다.

**불칸**: 로키 말이 맞습니다. 구분해야 합니다: (1) 스킬 주입(injection) 이벤트 — dispatch.py에서 자동 캡처 가능, 신뢰도 100%. (2) 스킬 실행(invocation) 이벤트 — Skill 도구 호출 시점, 현재 자동 캡처 불가. 수동 API 호출로만 가능. 우선 (1)부터 구현하고 (2)는 수동 로깅 엔드포인트를 만들어두는 것이 현실적입니다.

**아르고스**: 테스트 관점에서 추가하겠습니다. 로깅 실패가 메인 플로우를 막아선 안 됩니다. try/except로 완전히 격리하고, 로거 자체의 에러는 `/home/jay/workspace/memory/logs/errors.jsonl`에만 기록해야 합니다. 또한 동일 스킬이 같은 태스크에서 여러 번 호출될 경우 중복 집계 처리 로직도 필요합니다 — count 필드로 증분하거나 각각 별도 레코드로 남기거나.

**이리스**: 프론트 입장에서 한 마디만요. 로깅 데이터가 어떤 형태로 오느냐에 따라 집계 방식이 완전히 달라집니다. 각 레코드가 단일 호출이면 클라이언트에서 GROUP BY 해야 하는데, 87개 스킬 × 수천 레코드면 브라우저 성능 이슈가 생깁니다. 서버에서 집계된 summary를 제공하는 `/api/skill-stats` 엔드포인트가 있어야 합니다.

**헤르메스**: 정리하겠습니다. 저장 포맷은 JSONL로 확정. 훅 포인트는 두 단계 — dispatch.py에서 자동 캡처(injection 이벤트)와 수동 로깅 API(invocation 이벤트). 로거는 메인 플로우와 완전 격리.

---

### [토론 2] 대시보드 통합 설계

**이리스**: 새 탭 "스킬" 추가가 맞습니다. 현재 App.js 탭 구조를 보면 '조직뷰', '프로젝트뷰', '캠페인뷰', '시스템뷰', '토큰', '자동화', '기록', '집단지성', '히스토리' 9개 탭이 있습니다. '토큰' 탭 옆에 '스킬' 탭을 추가하면 자연스럽습니다. 시스템뷰 확장안은 이미 시스템뷰가 스케줄, CI, Tech Debt로 복잡한 상태라 추가 섹션 삽입이 어렵습니다.

**아테나**: UX 관점에서 설계를 제안합니다. 스킬 탭은 세 영역으로 나뉩니다. 상단 요약 카드 4개 — "전체 스킬 수", "이번 달 활성 스킬", "총 호출 횟수", "가장 많이 쓰인 스킬". 중단은 스킬 목록 테이블 — 스킬명, 카테고리, 상태(active/deprecated/unused), 기간별 호출 횟수(일/주/월/분기). 하단은 미니 바 차트로 top 10 스킬 사용량 시각화. 기간 필터는 토큰 뷰와 동일한 패턴(1d/7d/30d/90d 버튼)으로 UX 일관성을 유지합니다. 스킬 카드 클릭 시 모달로 상세 호출 로그 표시.

**로키**: 아테나 안에 구조적 문제가 있습니다. "이번 달 활성 스킬"과 "unused" 상태 표시는 애초에 데이터가 없으면 의미 없는 지표입니다. 지금 사용 추적이 제로인 상황에서 "unused"로 표시되는 스킬 82개를 보면 팀장이 뭐라 할 것 같습니까? "데이터를 더 수집하라"는 것이지, 대시보드 문제가 아닙니다. 빈 차트보다 나쁜 것은 오해를 부르는 차트입니다. 제안: 초기 3~4주는 데이터 수집 기간으로 지정하고, 스킬 탭에는 "스킬 목록 + 카테고리 + 상태"는 정적으로 보여주되, 사용 통계는 데이터가 N개 이상 쌓인 스킬에만 표시하는 조건부 렌더링으로 가야 합니다.

**아테나**: 로키 지적을 반영해서 수정합니다. 빈 상태(empty state) 디자인을 명확히 합니다 — 데이터가 없는 스킬은 회색으로 "추적 시작 전" 배지 표시, N=10 이상 된 스킬부터 통계 표시. 또한 task-timers.json과 연동해서 현재 실행 중인 태스크에서 어떤 스킬이 주입되었는지 실시간 표시 — "지금 활성" 뱃지.

**이리스**: task-timers.json 연동 방식 구체화하겠습니다. `/api/skill-stats` 엔드포인트가 skill-usage.jsonl을 읽어 집계하고, `/api/active-skills` 엔드포인트가 task-timers.json에서 running 상태 태스크의 skill_list 필드를 읽어 반환합니다. 폴링 주기는 30초 — SystemView의 fetchSchedules와 동일 패턴.

**아르고스**: 탭 추가 시 기존 탭 레이아웃 깨지는 것 테스트해야 합니다. 현재 9개 탭이 모바일 뷰에서 이미 좁습니다. 탭이 10개가 되면 shortLabel 처리가 필요합니다. '스킬'은 한 글자면 되니 shortLabel: '스킬'로 충분합니다.

**헤르메스**: 확정합니다. 새 탭 '스킬' 추가, shortLabel: '스킬'. 조건부 렌더링으로 데이터 미수집 스킬은 empty state 처리. 폴링 30초 주기.

---

### [토론 3] 데이터 구조

**불칸**: skill-usage.jsonl 레코드 스키마를 제안합니다.

```json
{
  "ts": "2026-03-27T09:15:00.000000+00:00",
  "event_type": "injection",
  "skill_name": "frontend-design",
  "skill_category": "개발/UI",
  "task_id": "task-1112.1",
  "team": "dev1",
  "agent": "이리스",
  "session_id": "abc123",
  "invocation_count": 1,
  "source": "dispatch_auto"
}
```

`event_type`은 `injection` | `invocation` | `manual` 세 값. `source`는 `dispatch_auto` | `skill_tool_call` | `manual_api`. 이렇게 하면 나중에 injection vs 실제 사용 비율을 분석할 수 있습니다.

**로키**: 스키마 설계에서 치명적인 누락이 있습니다. `skill_name`이 자유 텍스트입니까? 그러면 "frontend-design", "frontend_design", "FrontendDesign" 세 가지가 동일 스킬로 집계되지 않는 사고가 납니다. 반드시 `skill_id`(정규화된 slug)와 `skill_name`(display name)을 분리해야 합니다. 그리고 `invocation_count`를 레코드에 넣으면 "1회 호출 = 1레코드"인지 "누적 카운트"인지 혼동됩니다. 레코드는 이벤트 단위(1레코드 = 1이벤트)로 단순화하고, count는 집계 시 계산해야 합니다. 추가로 `duration_ms` 필드도 넣어야 나중에 스킬 실행 시간 분석이 가능합니다. engine_usage_2026-03.jsonl이 `duration_sec`을 갖고 있는 것과 동일한 이유입니다.

**불칸**: 로키 지적 반영해서 수정합니다.

```json
{
  "ts": "2026-03-27T09:15:00.000000+00:00",
  "event_type": "invocation",
  "skill_id": "frontend-design",
  "skill_name": "Frontend Design",
  "skill_category": "개발",
  "skill_subcategory": "UI/UX",
  "task_id": "task-1112.1",
  "team": "dev1",
  "agent": "이리스",
  "session_id": "abc123",
  "duration_ms": null,
  "source": "skill_tool_call",
  "success": true,
  "metadata": {}
}
```

`skill_id`는 `/home/jay/.agents/skills/` 디렉토리명과 동일하게 강제합니다.

**아테나**: 스킬 카테고리 분류 기준을 정해야 합니다. 현재 87개 스킬을 보면 기능 영역으로 나눌 수 있습니다:
- **개발**: `frontend-design`, `fullstack-builder`, `vercel-react-best-practices`, `supabase-postgres-best-practices`, `claude-api`, `mcp-builder` 등
- **마케팅**: `marketing-strategist`, `social-content`, `ad-creative`, `copywriting`, `blog-writer` 등
- **분석/리서치**: `research-prompt`, `cross-verified-research`, `analytics-tracking`, `competitor-analyst` 등
- **CRO/전환**: `page-cro`, `popup-cro`, `form-cro`, `signup-flow-cro`, `paywall-upgrade-cro` 등
- **SEO**: `seo-audit`, `programmatic-seo`, `naver-seo`, `blog-dominance`, `ai-seo` 등
- **AI/에이전트**: `subagent-driven-development`, `agent-meeting`, `swing-trace`, `swing-mortem` 등
- **콘텐츠**: `book-writer`, `satori-cardnews`, `canvas-design`, `gemini-image`, `hybrid-image` 등
- **QA/검증**: `systematic-debugging`, `tdd-enforcement`, `adversarial-review`, `webapp-testing` 등
- **유틸리티**: `pdf`, `docx`, `find-skills`, `skill-creator`, `3docs-create` 등

카테고리는 `skill-categories.json` 파일로 별도 관리합니다.

**이리스**: 집계/필터링 방식은 서버 사이드로 결정했습니다. 클라이언트에서 수천 레코드를 GROUP BY하면 React 렌더링 성능이 떨어집니다. `/api/skill-stats?period=7d&category=마케팅` 형태로 파라미터를 받아서 server.py에서 파이썬으로 집계 후 반환합니다. 응답 형식:

```json
{
  "summary": {
    "total_skills": 87,
    "active_skills": 12,
    "total_invocations": 47,
    "top_skill": "frontend-design"
  },
  "by_skill": [
    {
      "skill_id": "frontend-design",
      "skill_name": "Frontend Design",
      "category": "개발",
      "status": "active",
      "invocations": 8,
      "last_used": "2026-03-26T14:23:00Z"
    }
  ],
  "by_category": { "개발": 15, "마케팅": 7 },
  "by_period": { "2026-03-27": 3, "2026-03-26": 12 }
}
```

**아르고스**: 테스트 케이스를 미리 정의합니다. (1) skill-usage.jsonl이 없을 때 `/api/skill-stats`가 빈 summary를 반환하는지. (2) 잘못된 period 파라미터 입력 시 400 에러 반환하는지. (3) skill_id가 skill-categories.json에 없는 미등록 스킬이 들어왔을 때 "기타" 카테고리로 처리되는지. (4) 동시에 여러 에이전트가 skill-usage.jsonl에 append할 때 파일 손상이 없는지 — fcntl.flock으로 파일 락 테스트 필요.

**로키**: 마지막으로 한 가지 더. `skill-categories.json`을 별도 파일로 관리하자고 했는데, 이 파일과 `/home/jay/.agents/skills/` 디렉토리 목록이 sync가 안 맞을 때 어떻게 처리합니까? 새 스킬이 설치되면 자동으로 카테고리 파일에도 반영되어야 합니다. 아니면 server.py 시작 시 두 소스를 비교해서 불일치 스킬을 "미분류"로 자동 추가하는 로직이 필요합니다. 이것 없이 배포하면 운영 중에 KeyError가 납니다.

**불칸**: 맞습니다. server.py의 `reload_all()` 패턴처럼 startup 시 skill-categories.json과 실제 설치된 스킬 목록을 비교하는 `reconcile_skill_registry()` 함수를 추가하겠습니다. 불일치 스킬은 "미분류" 카테고리로 자동 등록하고 `/home/jay/workspace/memory/logs/errors.jsonl`에 경고 로그를 남깁니다.

**헤르메스**: 데이터 구조도 확정합니다.

---

## 합의 사항

1. **저장 포맷**: `skill-usage.jsonl` (append-only JSONL, `/home/jay/workspace/memory/skill-usage.jsonl`)
2. **훅 포인트 2단계**: dispatch.py 자동 캡처(injection 이벤트, 신뢰도 100%) + 수동 로깅 API(invocation 이벤트, `/api/skill-log` POST 엔드포인트)
3. **레코드 스키마 확정**: `ts`, `event_type`, `skill_id`, `skill_name`, `skill_category`, `skill_subcategory`, `task_id`, `team`, `agent`, `session_id`, `duration_ms`, `source`, `success`, `metadata` 14개 필드
4. **skill_id 정규화**: `/home/jay/.agents/skills/` 디렉토리명과 동일 슬러그로 강제
5. **스킬 카테고리**: 9개 대분류 (개발/마케팅/분석·리서치/CRO·전환/SEO/AI·에이전트/콘텐츠/QA·검증/유틸리티), `skill-categories.json`으로 별도 관리
6. **대시보드**: App.js에 새 탭 `{ id: 'skills', label: '스킬', shortLabel: '스킬' }` 추가 — '토큰' 탭 우측에 배치
7. **집계**: 서버 사이드, `/api/skill-stats?period=&category=` 엔드포인트 신설
8. **실시간 연동**: `/api/active-skills` 엔드포인트 신설, task-timers.json에서 running 태스크의 skill_list 읽어 반환, 30초 폴링
9. **조건부 렌더링**: 호출 이력 10회 미만 스킬은 통계 차트 미표시, "추적 시작 전" 배지 처리
10. **스킬 레지스트리 정합성**: server.py 시작 시 `reconcile_skill_registry()` 실행, 불일치 스킬 자동 "미분류" 등록
11. **로거 격리**: try/except로 로깅 실패가 메인 플로우 차단 금지, 로거 에러는 errors.jsonl에만 기록
12. **파일 동시 접근**: fcntl.flock으로 skill-usage.jsonl 쓰기 락 처리

---

## 미해결 사항

- **실제 Skill 도구 호출 자동 캡처**: Claude 내부 Skill 도구 호출은 현재 기술적으로 자동 인터셉트 불가. 수동 API 방식의 실제 기록률(compliance rate)을 모니터링 후 3개월 내 재검토 필요
- **task-timers.json의 skill_list 필드 신설 여부**: 현재 task-timers.json 스키마에 skill_list 필드가 없음. dispatch.py 수정 시 기존 태스크 하위 호환성 검토 필요
- **87개 스킬 초기 카테고리 분류 작업**: 수동 분류 작업 담당자 미정 (추정 1~2시간)
- **스킬 deprecated 처리 기준**: 마지막 호출 후 몇 주가 지나면 deprecated 배지를 붙일지 기준 미정 — 로키 제안으로 다음 미팅 안건으로 이월

---

*기록: 헤르메스 / 2026-03-27*
