# 7편 CloddsBot — 실행 기록

작성: 토니 브라운 · 2026-09-22 KST 03:58~04:38
글: [`docs/tutorials/a/07-cloddsbot.md`](../../../docs/tutorials/a/07-cloddsbot.md)

---

## 환경

| | |
|---|---|
| OS | macOS 26.6.2 · arm64 |
| Node.js | 26.8.1 · npm 11.19.0 |
| 소스 | `alsk1992/CloddsBot` · 로컬 HEAD `c930628c` (2026-09-12) · 패키지 `clodds` 1.9.1 |
| 작업 사본 | 스크래치패드 `work/cloddsbot` (원본 `cl7` 은 그대로 둠) |
| 상태 디렉터리 | `work/state` (`CLODDS_STATE_DIR`) — `~/.clodds` 를 쓰지 않도록 분리 |
| 모델 경로 | OpenRouter (`ANTHROPIC_BASE_URL=https://openrouter.ai/api`) |
| 게이트웨이 포트 | **18789** (기본값. 점유돼 있지 않아 그대로 씀) |

## 건드리지 않은 것

> 엔진 pid 55526 · 웹 pid 48127 · 67039 · 71349 · 감시 pid 50367 · 3678 — **실행 전후로 생존 확인했다.**
> 돌고 있던 슬롯 12개는 조회조차 하지 않았다. weltiq 웹·엔진·DB 를 전혀 건드리지 않았다(이 편은 접점이 없어 우리 쪽 실행이 없다).
> 죽인 프로세스는 내가 띄운 CloddsBot 게이트웨이 2개(pid 22571 → 23283)와 내가 연 Terminal 창 1개(id 5897)뿐이다.

```
pid 55526 alive / pid 48127 alive / pid 67039 alive
pid 71349 alive / pid 50367 alive / pid 3678 alive
```

## 하지 않은 것 (정직 규칙)

- **지갑을 만들지 않았다.** Solana 키페어 생성 0건, 온체인 트랜잭션 0건
- **예측시장·거래소 계정을 만들지 않았다.** API 키 0장
- **주문 0건.** 이 봇에는 페이퍼 경로가 없어 주문을 내면 실거래다 — 그래서 S5·S6 를 못 했다
- **`clodds mcp install` 을 실행하지 않았다** (사용자 `~/.claude.json` 을 건드리는 명령. 실제로는 존재하지도 않았다)
- README 에 적힌 Solana 토큰 주소를 조회하지 않았다

---

## 1. 설치·빌드

| 한 일 | 결과 |
|---|---|
| `npm install --no-audit --no-fund` | **13초** · `added 1737 packages` · EXIT 0 · `node_modules` 1.7GB |
| `npm run build` | **10초** (첫 회) · `dist/` 22MB · 파일 101개 · EXIT 0 |
| `rm -rf dist && npm run build` | **3초 · EXIT 0 · `dist/` 800K, `skills/` 하나뿐** ← `.tsbuildinfo` 가 루트에 있어 tsc 가 아무것도 안 내보냄 |
| `rm -f .tsbuildinfo && npm run build` | **10초** · `dist/` 22MB 복구 · `dist/index.js` · `dist/cli/index.js` 확인 |

npm 캐시는 이미 5.2GB 차 있었다. **캐시가 빈 상태의 설치 시간은 미확인.**

## 2. 모델 경로 (OpenRouter)

| 시도 | 결과 |
|---|---|
| `curl https://openrouter.ai/api/v1/messages` + `model: anthropic/claude-sonnet-4-6` | HTTP **200** · `"type":"message"` · `model: anthropic/claude-sonnet-4.6` · cost **$0.000102** |
| 같은 주소 + `model: claude-sonnet-4-6` (접두사 없음) | HTTP **200** · 같은 모델로 해석됨 · cost $0.000264 → **접두사 불필요** |
| `claude-opus-4-6` / `claude-haiku-4-5-20251001` (접두사 없음) | 둘 다 HTTP 200 · 각각 `anthropic/claude-opus-4.6` · `anthropic/claude-haiku-4.5` |
| `https://openrouter.ai/api/v1/v1/messages` | HTTP **404** `{"error":{"message":"Not Found","code":404}}` |
| SDK `new Anthropic({apiKey})` + `ANTHROPIC_BASE_URL=https://openrouter.ai/api/v1` | 401 (SDK 가 `/v1/messages` 를 덧붙여 이중 v1) |
| SDK + `ANTHROPIC_BASE_URL=https://openrouter.ai/api` | **OK** · `client.baseURL = https://openrouter.ai/api` · 응답 `anthropic/claude-haiku-4.5` |
| `.env` 에만 적고 셸에 이미 `ANTHROPIC_BASE_URL` 이 있는 경우 | **무시됨** — dotenv 는 기존 env 를 덮어쓰지 않는다. 증상은 401 `API key is invalid` |

**소스 수정 0줄.** 운영자 메모의 "모델 이름에 `anthropic/` 접두사가 필요하다"는 `/v1/messages` 경로에서는 **틀렸다** — 접두사 없이 그대로 통한다. 반대로 주소 끝의 `/v1` 은 **빼야** 한다. 둘 다 실호출로 확인했다.

참고: `src/models/adaptive.ts:58` 의 `stripProviderPrefix()` 가 `anthropic/` 을 떼어 버리므로, 접두사를 쓰려면 오히려 그쪽을 고쳐야 한다.

## 3. 기동

```
[04:09:41.923] Custom condition handler registered      ← 첫 줄
[04:09:43.504] Loaded 121 skills
[04:09:43.725] Gateway started  port: 18789
[04:09:43.726] Clodds is running!                        ← 1.8초
[04:09:43.890] Manifold: WebSocket connected
[04:09:44.053] Polymarket WebSocket connected
[04:09:48.631] Market index sync completed
                 indexed: 692
                 polymarket 300 / kalshi 300 / manifold 92 / metaculus 0
[04:09:48.631] WARN Market index sync failed  platform: "metaculus"
[04:09:54.361] Local embedding model loaded  Xenova/all-MiniLM-L6-v2
[04:12:44.579] ERROR PredictIt fetch error:              ← 기동마다 난다
```

- `/webchat` HTTP **200** · **인증 없음** (`CLODDS_TOKEN` 미설정 → `requireAuth` 통과, `src/gateway/server.ts:237-244`)
- `/health` HTTP 200 `{"status":"healthy",... "database":{"status":"pass","latencyMs":1}}`
- `/dashboard` HTTP 200 · 제목 `Clodds Performance Dashboard` · 본문 **"Failed to load performance data. Make sure trading is enabled."**
- 재기동 뒤 색인은 DB 에 남아 있었다 (692 → 이후 694)

### `clodds doctor` (CLI)

```
  ! Config file: Not found
  ! .env file: Not found (using env vars only)
  ✗ Database: Error: rows is not iterable
  ✗ Anthropic: HTTP 401
  0 passed, 2 warnings, 2 failed
```

- Anthropic 실패는 **오탐**이다 — `src/doctor/index.ts:263` 이 `https://api.anthropic.com/v1/messages` 를 박아 놓아 `ANTHROPIC_BASE_URL` 을 보지 않는다
- `rows is not iterable` 은 DB 가 아직 없는 첫 실행에서 나고, 그 뒤 마이그레이션 15개가 정상 적용됐다
- **진단 출력이 끝난 뒤 프로세스가 종료되지 않았다.** 4분 기다리다 끊음

## 4. 121 스킬의 근거 (운영자 질문 ①)

**찾았다.** 최상위 `skills/` 가 아니라 **두 단계 아래**다.

```
$ ls -d src/skills/bundled/*/ | wc -l          → 121
$ find src/skills/bundled -name SKILL.md | wc -l → 121
기동 로그                                        → Loaded 121 skills
$ head -1 docs/SKILLS_CATALOG.md                → # Skills Catalog (119 Skills)
카탈로그 표의 행 수                              → 119
디스크에 있으나 카탈로그에 없는 것               → dex, rust-hft-arbitrage
```

## 5. 시나리오 D (= S4) — 키 0장으로 예측시장 가로지르기

전부 WebChat 에서 대화로. 모델은 `claude-haiku-4-5-20251001` (설정 핫 리로드로 opus → haiku).

| 프롬프트 | 결과 |
|---|---|
| `Search the market index for bitcoin markets…` | Manifold · Polymarket 이 한 표에 섞여 나옴. Manifold "Bitcoin $87,500 in Sept 2026?" 24h **$598.45**. 모델이 스스로 "가격 예측이 아니라 정책·세금 시장이 나왔다"고 먼저 밝힘 |
| `Run the cross-platform arbitrage scan… No trades.` | 도구 `find_arbitrage` 실행 **3801ms** → **0.5% 이상 차익 없음**. 막는 요인 6개를 답에 적음 |
| `…threshold lowered to 0.1%, and also show me the market index stats` | 색인 **Polymarket 300 · Kalshi 300 · Manifold 94 · Metaculus 0 · 합계 694**. 도구 `find_arbitrage` **3527ms** → 0.1% 에서도 없음 |

화면에 `Running tool: … / Finished tool: … (Nms)` 가 그대로 노출된다.

## 6. 안전장치 실측 (시나리오 S7 대체)

**주문 경로가 아니라 모듈 단위 측정이다.** `src/trading/safety.ts` 와 실제 sqlite 를 그대로 쓰고, 케이스마다 `CLODDS_STATE_DIR` 을 갈라 상태가 안 섞이게 했다.
스크립트: [`probe/case.mjs`](probe/case.mjs) · 원문 출력: [`probe/safety-probe.out`](probe/safety-probe.out)

> **첫 시도는 틀렸다.** `createDatabase()` 가 인자를 받지 않고 전역 상태 디렉터리의 DB 를 쓴다는 것을 몰라, 케이스 간에 상태가 새어 c1 이 c2 의 킬 스위치를 물려받았다(`"MANUAL KILL: probe: cooldown test"`). 그리고 같은 DB 에 두 번째 매니저를 만들면 `UNIQUE constraint failed: safety_state.id` 로 죽는다 — 이 DB 는 비동기 지연 초기화라 `db.query` 가 빈 배열을 돌려주고 `INSERT` 분기를 타기 때문이다. 상태 디렉터리를 케이스마다 분리해 다시 쟀고, **아래 숫자는 다시 잰 쪽이다.**
> 새어 들어갔던 게이트웨이 DB 의 `safety_state` 는 확인 결과 `trading_enabled=1` 로 멀쩡했고, 그래도 명시적으로 초기화해 두었다.

| 케이스 | 결과 |
|---|---|
| **c1 기본값** | `tradingEnabled=true · peakValue=10000 · currentValue=10000` — 내 자본과 무관한 하드코딩 (`safety.ts:156-163`) |
| **c1 자본 미등록 상태에서 $80 주문** | `{"allowed":true}` — 집중도 분모가 $10,000 이라 0.8% 로 계산 |
| **c1 `updatePortfolioValue(200)` 직후** | `drawdown 98.0% · tradingEnabled false` · `"Drawdown 98.0% exceeds 20% limit"` · 같은 주문 재시도 거부 · `resumeAt` 4시간 뒤 — **손실 0원인데 트립** |
| **c2 수동 킬 + 쿨다운 1.5초** | 직후 `false` → 1.8초 뒤 **`false`** — 수동 킬은 자동 재개 없음 (`safety.ts:487 resumeAt = undefined`) |
| **c3 일일 손실 (비율 한도 끔, 자본 $200)** | −$100 → `true` · −$200(전액) → `true` · **−$520(자본의 260%) → `false`** — 기본 한도가 달러 절대값 $500 |
| **c4 수동 킬 해제** | `killSwitch` → `false` · `resumeTrading()` → `true` · 이후 `canTrade()` `true` |
| **c5 자동 브레이커 + 쿨다운 1.5초** | 트립 `"Drawdown 98.0% exceeds 20% limit"` → 직후 `false` → **1.8초 뒤 `true`**, `disabledReason` 도 `undefined` 로 지워짐 — **사람 확인 없이 자동 재개** (`safety.ts:336-347`) |

기타:
- `autoCloseOnBreaker: false` 가 기본 (`safety.ts:148`) — 트립은 신규 주문 금지이지 청산이 아니다
- `tests/trading-safety.test.ts` → **21/21 pass · 759ms**. 다만 이 테스트는 `src/utils/kelly` 와 `src/execution/circuit-breaker.ts` 를 검사한다
- `grep -rln "trading/safety" tests` → **빈 출력.** `src/trading/safety.ts` 를 부르는 테스트가 저장소에 없다

## 7. MCP 통로 (운영자 질문과 계획서 전제 검증)

| 확인 | 결과 |
|---|---|
| `node dist/cli/index.js mcp` (stdio 로 initialize/tools_list 투입) | **MCP 서버가 아니다.** commander 도움말 출력. 서브커맨드는 `list / add / remove / test` — **CloddsBot 이 남의 MCP 를 소비하는 쪽** |
| `clodds mcp install` | **없는 서브커맨드** (README 는 있다고 적어 뒀다) |
| `grep -rn "mcp/server" src` | 빈 출력 — `startMcpServer()` 를 부르는 곳이 없다 |
| `startMcpServer()` 직접 호출 | **동작한다.** `initialize` → `{"name":"clodds","version":"0.1.0"}` · `tools/list` → **118개** |
| 118 vs 121 | 빠진 셋: `markets` · `opportunity` · `pairing` (`opportunity` 는 `gates.envs` 로 `POLY_API_KEY` 등을 요구) |
| 도구 description | 전부 `` `Clodds skill: ${name}` `` (`src/mcp/server.ts:70`) — 각 SKILL.md 의 실제 설명을 쓰지 않는다. 인자는 자유 문자열 `args` 하나 |

## 8. 화면 캡처

`shots/` 6장. **전부 CloddsBot 자신의 화면이다. weltiq 화면은 0장** — 이 편은 접점이 없어 우리 쪽 실행이 없었다(§3.3 단서 적용).

| 파일 | 무엇 |
|---|---|
| `01-webchat-first-open.png` | 설치 완료 — WebChat 첫 화면, 바로가기 넉 장 |
| `02-agent-markets.png` | 시장 색인 검색 — Manifold·Polymarket 이 한 표에 |
| `03-agent-arbitrage.png` | **대표 그림** — `find_arbitrage` 도구 호출 추적(3801ms)과 결과 |
| `04-agent-arbitrage-low.png` | 문턱 0.1% 재스캔 + 색인 통계 694 |
| `05-chat-doctor.png` | 채팅 `/doctor` — "API Connectivity ✓ Pass" (같은 시각 로그에는 실패 2건) |
| `06-dashboard-empty.png` | `/dashboard` — "Failed to load performance data" (실패 화면 그대로) |

**터미널 캡처는 없다.** 이 환경에 화면 기록 권한이 없어 `screencapture` 가 `could not create image from rect` 로 거부했다. 터미널 출력은 글 본문에 코드 블록으로 원문 그대로 넣었다.
캡처는 헤드리스 Chromium(playwright, 1440×950/1000, DPR 2)으로 찍었다. 키·토큰이 화면에 나오는 자리가 없어 가릴 것도 없었다.
슬래시 명령 팔레트(`/` 입력)는 DOM 에는 뜨는데 헤드리스 캡처에 안 찍혀 그 장은 뺐다 — 헤드리스 특유의 문제일 수 있어 버그로 적지 않았다.

## 9. 비용 (운영자 질문 ④)

| | |
|---|---|
| 상한 | $1 |
| OpenRouter 누적 사용량 (04:13 기준) | 17.149243638 |
| OpenRouter 누적 사용량 (04:31 기준) | 17.349175888 |
| **차액** | **≈ $0.20** |

`usage_daily` 도 같은 폭(2.027 → 2.227)으로 움직였다. 다만 이 키는 공용이라 **같은 시간대에 다른 작업이 썼다면 그만큼 섞인다** — 그 분리는 못 했다. 대부분은 기본 모델이 `claude-opus-4-6` 이던 초반 두 턴에서 나갔을 것으로 보이며, haiku 로 내린 뒤 turn 당 비용은 눈에 띄게 줄었다. 상한 $1 안이다.

CloddsBot 자체의 `usage_records` 테이블은 **비어 있었다** — 테이블과 `/usage` 스킬은 있는데 WebChat 에이전트 경로가 거기에 쓰지 않는다. (글 본문에는 넣지 않았다 — 다른 경로에서는 쓸 수도 있어 단정하지 않았다.)

## 10. 정리·뒷정리

- CloddsBot 게이트웨이(pid 23283)를 정지했다. 포트 18789 해제 확인
- 내가 연 Terminal 창(id 5897)은 그 창만 닫았다. **다른 창·탭은 건드리지 않았다**
- 프로브가 `~/.clodds` 를 만들었다 (작업 시작 시점에는 없었다). 제거했다
- 원본 소스 `scratchpad/cl7` 은 수정하지 않았다. 모든 작업은 `work/cloddsbot` 사본에서 했다

## 11. 미확인으로 남긴 것

- `trading.dryRun: true` 경로가 실제로 주문을 막는지 — **안 돌렸다.** 키가 있어야 그 경로에 들어간다
- 실제 체결이 붙었을 때 `updatePortfolioValue()` 가 누가 얼마나 자주 불러 주는지 — 6절 ②③ 의 체감이 여기에 달려 있다
- npm 캐시가 빈 상태의 설치 시간
- 텔레그램·디스코드 채널 경로 (봇 토큰을 만들지 않았다)
- `clodds doctor` 가 왜 종료되지 않는지

---

## 12. 독자판(v2) 재편 때 본문에서 내린 것 (2026-09-24)

기사를 v2 포맷(≤3,000어절)으로 줄이면서 본문에서 뺀 것 중 **이 기록에 아직 없던 것**만 옮겨 적는다.
숫자·명령·출력은 하나도 버리지 않았고, 아래는 전부 본문 밖으로 내린 표·목록이다.

### 12.1 번들 스킬 121개의 분야별 구성 (카탈로그 119 기준)

| 분야 | 개수 | 예 |
|---|---:|---|
| DeFi & DEX | 12 | pancakeswap · bridge · slippage · mev |
| 선물·무기한 | 8 | hyperliquid · drift · binance-futures · bybit-futures |
| **예측시장** | **11** | polymarket · kalshi · manifold · metaculus · betfair |
| Solana DeFi | 12 | jupiter · raydium · orca · meteora |
| 포트폴리오·리스크 | 11 | |
| 데이터·피드 | 10 | |
| AI·전략 | 15 | |
| 인프라 | 16 | |
| 소셜·아이덴티티 | 9 | |
| 유틸리티 | 13 | |
| 보안 | 2 | |

**주식이 한 개도 없다** — 한국도 미국도. 본문에는 이 판정 한 줄과 상위 네 분야만 남겼다.

### 12.2 돌리지 않은 시나리오 (본문 표를 그대로 내림)

| | 왜 안 돌렸나 |
|---|---|
| **S1** 글로벌 종목 하나 (BTC-USD 90일) | 이 도구는 **봉을 주는 물건이 아니다.** 예측시장 배당과 온체인 시세를 다룬다 |
| **S2** 한국 종목 (005930) | **한국 자료가 없다.** 스킬 121개에 주식이 한 개도 없다 |
| **S3** 내 실행 기록 복기 | 남기는 기록이 **자기 체결**이고, 체결하려면 실거래 키가 필요하다. 페이퍼 모드가 없어 **못 했다** |
| **S5** 주문 한 번 왕복 | **못 했다.** 페이퍼 경로가 없고 주문을 내면 실제 자금이 나간다 |
| **S6** 고리 한 바퀴 | S5 가 막혀 성립하지 않는다 |
| **S7** 중지 조건 발동 | **§6 안전장치 실측으로 모듈 단위 대체.** 주문 경로를 통과시킨 실측은 아니다 |
| **S8** 끊고 다시 붙이기 | 포지션이 없어 복구할 것이 없다. 다만 색인 DB 는 재기동 뒤에도 남아 있었다 (692 → 694) |
| **S9** 수수료 바꿔 다시 재기 | 체결 기록이 없다 |
| **S10** 둘·셋 나란히 돌리기 | 이 편의 범위 밖 (별도 트랙) |

본문에는 이 표 대신 "주문을 내 보지 못한 것이 이 글의 제일 큰 구멍" 한 문단만 남겼다.

### 12.3 문서와 코드가 어긋난 자리 다섯 (본문 표 → 본문에서는 한 줄 나열로)

| 문서가 말한 것 | 실제 |
|---|---|
| `Skills Catalog (119 Skills)` | 디스크에 **121개** |
| `clodds mcp` → MCP 서버 기동 | 도움말만 출력 |
| `clodds mcp install` | **그런 서브커맨드가 없다** |
| `Expose all 121 skills as MCP tools` | 모듈을 직접 부르면 **118개**, 게다가 설명이 전부 같은 틀 |
| `clodds doctor` 의 Anthropic 검사 | `ANTHROPIC_BASE_URL` 을 보지 않는다 |

### 12.4 그 밖에 본문에서 내린 것

- 기동 스크립트 전문(`<details>` 블록) — 본문에는 `export` 두 줄만 남겼다. 전문은 다음과 같다.

```bash
#!/bin/zsh
export ANTHROPIC_API_KEY="<OpenRouter 키>"
export ANTHROPIC_BASE_URL="https://openrouter.ai/api"
export CLODDS_STATE_DIR="$HOME/clodds-state"     # ~/.clodds 대신 쓸 상태 디렉터리
export CLODDS_MODEL_STRATEGY=cost
cd /path/to/CloddsBot
exec node dist/index.js
```

- `src/tools/` 19개 전체 목록(browser·canvas·docker·email·exec·files·git·image·index·message·nodes·sessions·shell-history·sms·sql·transcription·web-fetch·web-search·webhooks) — 본문에는 성격 판정만 남겼다
- `npm warn deprecated` 원문 두 줄(`@hubbleprotocol/kamino-sdk@5.2.4` · `@aptos-labs/ts-sdk@2.0.1`)과 `added 1737 packages in 13s`
- `clodds mcp` 도움말 전문과 `tools/list` 앞 여섯 줄(`clodds_acp` · `clodds_agentbets` · `clodds_ai_strategy` · `clodds_alerts` · `clodds_analytics` · `clodds_arbitrage`)
- `src/trading/safety.ts` 의 `DEFAULT_CONFIG` · 초기 `state` · `canTrade()` · `killSwitch()` 코드 블록 — 본문에는 값만 인라인으로 남겼다 (§6 에 줄 번호와 함께 이미 있다)
- 화면 접근 표 중 `/health` · `/` 두 행 (§3 에 이미 있다)
