# DeepSeek Harness

> 181,214★ 레포에서 기법 다섯을 수확하려다, 이미 깔아 놓고 한 번도 안 켠 스킬 두 개에 전부 부딪혀 죽었습니다.

- 수치: 수확 5 → 0
- 사람이 보는 화면: https://aitryed.com/ko/teardowns/harness/deepseek-harness/
- 반대 언어: https://aitryed.com/en/teardowns/harness/deepseek-harness/index.md
- 저장소: https://github.com/deepseek-ai/deepseek-harness
- 이 사이트 안내(AI용): https://aitryed.com/ko/llms.txt

---

**「좋아요」가 18만 개 붙은 큰 창고입니다. 기법 다섯 개를 배워 오려 했고, 다섯 다 죽었습니다. 그리고 셋은 제가 이미 깔아 놓고 한 번도 안 켠 도구 두 개에 부딪혀 죽었습니다.**

> **[도판]** 플러그인 트리라는 말로는 안 보인다. 능력을 셋으로 쪼개 놓은 것이 이 물건의 정체다.
>
> dsh 의 부팅 구조. 베이스 번들이 행 78개를 한 번에 꽂고 그 위에 프로파일 번들, 홈 패치, 실행 시 오버레이가 차례로 덮어쓴다. 그렇게 만들어진 행들이 능력 이음매를 이루는데, 능력 하나는 인터페이스를 정의하는 행과 구현하는 행과 모델이 보는 툴 행 셋이 다 있어야 성립한다. 그래서 구현 행 하나만 갈면 나머지가 따라 움직인다.

## 이 물건의 정체 — 「프로그램」이 아니라 「부품 목록」입니다

AI 코딩 도구를 만들 때 보통은 **하나의 프로그램**으로 만듭니다.

이건 다릅니다. <strong>전부 갈아끼울 수 있는 «행 목록»</strong>으로 만들어 놨습니다.

어떤 AI를 쓸지, 어떤 도구를 줄지, 기록을 어떻게 남길지 — 심지어 **AI가 도는 방식 자체**도 **목록의 한 줄**입니다. 그 줄을 갈아끼우면 동작이 바뀝니다.

부팅도 「코드를 순서대로 실행」이 아니라 <strong>「설정 줄을 쌓는 일」</strong>입니다. 기본 묶음이 78줄을 깔고, 그 위에 골라 둔 묶음이 얹히고, 개인 설정이 덮고, 마지막으로 실행할 때 준 것이 덮습니다.

그리고 좋은 점 — **덮을 때 «섞지» 않고 «통째로 갈아치웁니다».** 그래서 **반쯤 섞인 상태가 없습니다.** 어떤 줄이든 **정확히 한 곳에서 온 값**입니다.

## 제일 배울 만했던 규칙 — 「능력 하나는 세 역할이 다 있어야 한다」

이 창고에서 제일 값나간 건 프로그램이 아니라 **나누는 단위에 대한 규칙**이었습니다.

새 능력을 하나 붙인다고 할 때, **세 조각이 다 있어야 한 덩어리로 칩니다.**

| 조각 | 무엇 |
|---|---|
| **정의** | 이 능력이 어떤 모양인지 선언 |
| **제공** | 실제로 그 일을 하는 부분 |
| **소비** | AI가 그걸 부를 수 있게 연결하는 부분 |

하나만 있으면 **덩어리로 안 칩니다.**

왜 이게 중요하냐면 — **「만들어 놓고 연결을 안 한」 상태**가 제일 흔한 사고이기 때문입니다. 물건은 있는데 아무도 못 부릅니다. 규칙을 이렇게 잡으면 **그 상태가 구조적으로 «미완성»으로 분류**됩니다.

## 또 하나 — 설정이 자기 출처를 스스로 적습니다

합쳐진 설정을 뽑아 보면, <strong>각 줄마다 「이건 어느 층에서 왔고 어느 층이 덮었다」</strong>가 주석으로 붙어 나옵니다.

설정이 여러 층에서 덮이는 구조에서 제일 흔한 질문이 <strong>「이 값 왜 이래?」</strong>인데, **설정 자신이 답을 들고 있습니다.**

## 그런데 설치부터 안 됐습니다

설명서 첫 줄의 설치 명령을 그대로 쳤습니다.

**300초 동안 아무 반응이 없다가 죽었습니다.**

유일한 우회 경로를 찾아서 깔았습니다. **첫 실행에서 크래시했습니다.**

## 그리고 수확 다섯 개가 전부 죽었습니다

여기가 이 편의 본론입니다. 다섯 중 **셋**이 같은 이유로 죽었습니다.

가져오려던 기법을 제 환경에 대 보니, **제가 이미 같은 걸 갖고 있었습니다.** 그것도 **도구 두 개 안에** 들어 있었습니다.

그런데 그 도구 둘이 — **깔려 있고, 한 번도 실행된 적이 없었습니다.**

나머지 둘은 「기법을 배워 오기」가 아니라 <strong>「제 코드 두 줄 고치기」</strong>로 접혔습니다.

## 그래서 없던 것은 도구가 아니라 「실행 이력」이었습니다

정리하면 이렇습니다.

| | 제가 생각한 것 | 실제 |
|---|---|---|
| 뭐가 없나 | **기법이 없다** | 기법은 있다 |
| 진짜 없는 것 | — | **그걸 켠 적이 없다** |

이게 이 칸에서 반복해서 나오는 결론입니다. **「없다」와 「있는데 안 켰다」는 완전히 다른 상태**인데, **파일 목록만 보면 구분이 안 됩니다.**

그리고 **안 켠 도구는 없는 도구와 결과가 똑같습니다.** 다만 **있다고 착각하게 만든다는 점에서 더 나쁩니다.**

## 인기 숫자를 어떻게 읽어야 하나

**「좋아요」 18만 개**는 큰 숫자입니다. 그런데 옆 숫자들을 같이 보면 그림이 달라집니다.

| 잰 것 | 값 | 무슨 뜻인가 |
|---|---|---|
| 좋아요 | 181,214 | **공개 9일 만의 숫자** |
| 계속 지켜보는 사람 | 792명 | **좋아요의 0.44%** |
| 질문·수정 제안 | **양쪽 다 닫혀 있음** | 밖에서 참여할 통로가 없음 |
| 정식 판 | **0개** (전부 시험판) | 호환 약속이 없음 |
| 기여 쏠림 | 1위 한 명이 **40.1%** | 사실상 소수가 만듦 |

「좋아요 18만」과 「지켜보는 사람 792명」의 간격이 이 창고의 실제 성격을 말해 줍니다. **많은 사람이 «들어는 봤고», 적은 사람이 «쓰고» 있습니다.**

그리고 **정식 판이 0개**라는 건 <strong>「내일 호환이 깨져도 약속을 어긴 게 아니다」</strong>라는 뜻입니다. 업무에 넣으면 안 되는 상태입니다.

## 그리고 라이선스에 함정이 하나

창고 자체는 자유롭게 쓸 수 있는 조건입니다. 그런데 **일부 기능이 조건이 다른 남의 부품을 끌고 옵니다.**

**창고 겉면의 조건만 보면 놓칩니다.** 실제로 설치될 때 뭐가 같이 딸려 오는지를 봐야 합니다.

## 여기부터는 자세한 기록입니다

**기법 다섯을 수확하려 했고, 다섯 다 죽었습니다.** DeepSeek Harness(`dsh`)는 코딩 에이전트를 통째로 해체해 **설정 파일의 행(row) 목록**으로 만들어 놓은 하네스입니다. 모델 어댑터도 툴 레지스트리도 세션 로그도, 심지어 에이전트 루프 자체도 행 하나로 교체됩니다. 설계는 진짜였습니다. 그런데 수확을 제 환경에 대 보니 다섯 중 셋이 **이미 `~/.claude` 안에 깔려 있으면서 한 번도 실행된 적 없는 스킬 두 개**에 부딪혀 무너졌고, 나머지 둘은 「기법 수입」이 아니라 「두 줄 수정」으로 접혔습니다. 없던 것은 도구가 아니라 **실행 이력**이었습니다. 덤으로 README 첫 줄의 설치 명령은 이 PC에서 300초를 침묵하다 죽었고(EXIT=124), 유일한 우회 경로도 첫 실행에서 크래시했습니다.



## 이것은 제품이 아니라 갈아끼울 수 있는 행 목록이다

**`dsh`는 DeepSeek이 만든 오픈소스 에이전트 하네스입니다.** 실행되는 프로그램의 정체는 「플러그인 트리」이고, 부팅은 코드를 순서대로 호출하는 일이 아니라 **설정 행을 쌓는 일**입니다. base 번들이 행 78개를 한 번에 꽂고, 프로파일이 고른 번들이 그 위에 얹히고, 홈에 둔 패치가 다시 덮고, 마지막으로 실행할 때 준 오버레이가 덮습니다. 패치는 행 id를 지목해 **설정을 병합하는 게 아니라 통째로 갈아치웁니다** — 반쯤 섞이는 상태가 없습니다.

런타임 규칙의 중심에는 불변식 하나가 더 있습니다. <strong>「모델에게 보이는 것은 곧 기록된 것」</strong> — 모델 요청에 도달하는 모든 것은 추가만 되는 세션 로그에서 재구성 가능해야 하고, 런타임이 그걸 강제합니다. 포크·재개·트랜스크립트·텔레메트리가 전부 이 한 스트림에서 파생됩니다.

| 항목 | 실측 (2026-08-22) |
|---|---|
| 규모 | 파일 7,903 · TypeScript 26MB · pnpm 워크스페이스 **227개**(그룹 50) |
| 소스 대 테스트 | src 1,389파일 10.53MB ↔ tests 909파일 **12.56MB** — 바이트로는 테스트가 더 크다 |
| 인기 | ★**181,214** · 포크 19,815 — 2026-08-13 공개, **9일 만의 숫자**다 |
| 참여 | 워처 792명(★의 0.44%) · Issues·PR **양쪽 닫힘** · Discussions 3,894 |
| 기여 | 커밋 13,145 · 1위 한 명이 **40.1%** · 상위 3명 61.1% |
| 릴리스 | 4개 **전부 rc** · 정식 태그 0 · 세션 포맷 버전 0(호환 약속 없음) |
| 라이선스 | MIT — 다만 서브에이전트 백엔드가 **비허용 라이선스 SDK**를 끌고 온다 |

**공개 9일에 커밋이 1.3만입니다.** 결정 기록 문서의 날짜가 두 달 전까지 거슬러 올라갑니다 — 비공개로 오래 만든 뒤 히스토리째 공개한 물건입니다. 여기서 값을 치르고 배운 것 하나: **★ 18만과 「검증된 물건」은 다른 축입니다.** 워처가 ★의 0.44%이고 Issues와 PR이 둘 다 닫혀 있으면, 그 18만은 사용량이 아니라 북마크 수입니다.

**그리고 이 숫자는 적어 두는 동안에도 움직였습니다.** 제 수집기가 08-20 07:11에 잡은 값이 170,925, 뜯기 시작한 08-22 아침이 181,170, 위 표를 채운 게 181,214, 이 문단을 쓰는 지금이 **181,723**입니다. 이틀에 **+10,798**, 이 글을 쓰는 몇 시간에 **+553**. 그래서 위 표의 ★는 값이 아니라 **찍은 시각**으로 읽어야 합니다. 별을 판단 근거로 쓰지 않는 이유가 취향이 아니라는 걸, 이 레포가 스스로 보여 줬습니다.

## 능력 하나는 세 역할이 다 있어야 「이음매」다

이 레포에서 제일 값나가는 규칙은 플러그인이 아니라 **능력을 쪼개는 단위**입니다.

| 기법 | 무엇 |
|---|---|
| **이음매 3역 강제** | 능력 하나는 인터페이스를 선언하는 **정의**, 구현하는 **제공**, 쓰는 **소비**(대개 모델이 보는 툴) 세 역할이 다 있어야 이음매다. 한 역할만으로는 이음매가 아니라고 규칙이 못박혀 있고, 패키지 이름이 곧 역할이다 |
| **행 출처 주석** | 합성된 설정을 덤프하면 구간마다 `# == 어느 계층, patched by 어느 계층` 이 붙어 나온다. 어느 층이 마지막으로 썼는지를 설정 자신이 적는다 |
| **끈 것을 자리에 남긴다** | 프로파일이 base의 행을 지우지 않고 `disabled: true` 로 덮는다. 「없다」와 「꺼놨다」가 구분된다 |
| **Code Mode** | 툴 스키마 전부를 모델에게는 `run_code` 하나로 접어 보여주고, 프로그램 안에서 하위 호출을 돌린다. 하위 호출은 각각 기록되지만 모델 히스토리에는 바깥의 정리된 결과만 들어간다 |
| **생성 후 재검증 문서** | 카탈로그·모듈 그래프를 코드에서 생성하고, 같은 스크립트를 `--check` 로 다시 돌려 커밋된 바이트와 일치하는지 본다 |
| **결정 기록을 경로로 관리** | 상태가 바뀌면 폴더를 옮긴다(제안/구현됨/기각/보관). 중앙 색인 파일은 **금지**다 |

**이 중 「행 출처 주석」과 「disabled 행」 둘은 제가 코드를 읽어서는 못 봤을 것들입니다.** 실제로 부팅시켜 덤프를 받아 보고서야 눈에 들어왔습니다.

## 깨본 결과 — README 첫 줄이 지금 성립하지 않는다

**문서가 시키는 대로 깔면 안 깔립니다.** 격리 폴더에서 `npm install @deepseek-ai/dsh` 를 300초 제한으로 돌렸더니 **로그 파일 0바이트**로 침묵하다 타임아웃(EXIT=124)했습니다. 같은 명령에 `--legacy-peer-deps` 만 붙이면 30초에 끝납니다 — 네트워크가 아니라 peer 의존 그래프 되짚기가 원인이라는 뜻입니다. 실제로 설치하면 430패키지 260MB가 깔리는데, 첫 실행이 `ERR_MODULE_NOT_FOUND` 로 죽습니다. 부팅 글루 패키지의 `dependencies` 가 하나뿐이고 **부팅에 반드시 필요한 아홉 개를 전부 `peerDependencies` 로 선언**해 뒀는데, 그 플래그가 정확히 peer를 안 깔기 때문입니다.

여기서 아이러니가 있습니다. 위에서 칭찬한 **「이음매는 세 역할이 다 있어야 한다」는 규칙이, 발행된 패키지에서는 정의 역할만 빠진 채로 나갔습니다.** 규칙은 저장소 안에서만 강제되고 발행 파이프라인에는 걸려 있지 않았습니다.

간판 셋도 확인해 보니 이랬습니다.

- **「fail-closed 프로세스 격리」는 파일 쓰기 울타리입니다.** 레포 자신의 문서가 <strong>"파일 효과가 정책 어휘의 전부"</strong> 라고 적어 뒀고, 실제 프로파일에 네트워크 차단 인자가 없습니다. 기본 모드에서 샌드박스된 툴이 `~/.ssh` 를 읽고 제한 없는 네트워크로 내보낼 수 있습니다.
- **「모델에게 보이는 것은 곧 기록된 것」은 쓰기 측만 보장합니다.** 중단된 턴 뒤에 기록기가 낡은 카운터로 순번을 재시작해 로그가 통째로 읽기 불가가 되고, 한 보고에서는 **이벤트 127개가 실제로 사라졌습니다.** 로그가 유일한 원본이라 기록기 버그 하나가 세션 전체를 파괴합니다 — 단일 원본 설계의 대가가 실현된 자리입니다.
- **게이트 39종 중 일부는 어떤 CI에도 배선돼 있지 않습니다.** 그리고 그 미배선분이 지금 기본 브랜치에서 빨간불입니다. 39라는 숫자는 「돈다」가 아니라 「있다」는 뜻이었습니다.

> **[도판]** 설치 실측과 수확 판정은 다른 축이다. 도입을 막은 건 설치였고, 수확을 막은 건 제 창고였다.
>
> 설치를 세 경로로 재본 결과와 수확 다섯 종의 판정. 문서대로 하면 300초 침묵 후 타임아웃, 커뮤니티 우회는 설치는 되지만 첫 실행에서 크래시, 빠진 의존을 손으로 깔면 884밀리초에 정상 종료한다. 수확 후보 다섯은 두 개가 강등되고 세 개가 기각되어 남은 것이 없다.

## 그래도 부팅시켜 본 값은 있었다

**빠진 의존을 손으로 깔고 나서, 합성된 설정을 덤프했습니다.** 503줄 · 행 **135개** · 그중 `disabled: true` 가 **25개** · 출처 주석 24개가 세 종류로 갈려 나왔습니다(base가 쓴 것 / base를 프로파일이 덮은 것 / 프로파일이 새로 쓴 것). 코드를 읽어서는 이게 안 보입니다. **web 프로파일은 base의 행을 지우지 않고 자리에 남긴 채 끕니다** — 135행 중 25행이 그렇습니다.

이걸 제 쪽에 대 보니 정확히 반대였습니다. `~/.claude` 의 에이전트는 활성 46개 옆에 `.bak*` 24개와 `archive/` 7개가 흩어져 있어 **파일 목록만으로는 활성 개수를 셀 수가 없습니다.** 「끈 것」이 확장자 개명과 폴더 이동 두 방식으로 갈려 있기 때문입니다.

그리고 제 상시 지시문의 무게를 처음 재봤습니다 — 룰 20개 + 인덱스 메모리 + 스킬 99개 설명을 합쳐 **약 27,244 토큰**입니다. ⚠ 실제 토크나이저가 아니라 글자수 휴리스틱이라 **±30% 오차를 봐야 하는 추정치**입니다. 이 중 켜고 끌 수 있는 장치는 하나도 없습니다.

## 수확 다섯을 죽인 것은 제 창고에 있었다

제 판정을 깨는 단계에서 다섯 개가 차례로 무너졌습니다.

- **합성 설정 덤프 → 강등.** 산출물의 절반(크기 인벤토리)은 `context-budget` 스킬이 이미 합니다. 나머지 절반이 겨냥한 사고 — 설정 import가 폴더 위치에 따라 **경고 하나 없이 무시되어** 같은 파일이 5만 토큰과 0으로 갈린 일 — 은 이미 실측·전수 점검·상시 결정까지 끝나 메모에 박혀 있었습니다. 근사치 스크립트가 이미 있는 실측을 대체하려던 구도였습니다.
- **재생성 검증 게이트 → 강등.** 구멍은 진짜였지만 기법이 과했습니다. 대상 레포가 생성과 검증을 쪼갠 이유는 CI가 저장소에 쓰면 안 되기 때문인데, 개인사이트 `scripts/deploy.mjs` 는 **이미 마음껏 씁니다.** 여기 필요한 건 검증이 아니라 재생성이고, 빌드 앞에 생성기 두 줄을 부르면 끝납니다.
- **`disabled` 행 규약 → 기각.** `config-gc` 스킬이 이미 **부드러운 삭제 → 휴지통 폴더 → 사유 로그** 규약을 갖고 있습니다. 여기에 매니페스트를 하나 더 얹으면 규약 난립을 고치겠다며 **네 번째 규약**을 만드는 셈입니다.
- **의존 선언 가드 → 기각.** 퍼블리시 체크리스트에 같은 계열 항목이 이미 둘 있고, 결정적으로 **제가 퍼블리시하는 레포 중 npm 패키지가 0개**입니다. 「10분이라 사실상 무료」였던 게 아니라 이득이 0이라 무료였습니다.
- **룰 프로파일화 → 기각.** 범위를 좁히는 사다리는 이미 메모에 정본으로 있고, 전역 룰이 하위 세션까지 전부 전파돼 **끌 수단이 물리적으로 없다**는 것도 이미 실측돼 있었습니다. 여기서 「보류」로 두면 반년 뒤 같은 제안이 다시 옵니다.

**셋을 죽인 두 스킬은 둘 다 실행 이력이 0이었습니다.** `context-budget` 은 도입 출처 메모 한 줄 말고 흔적이 없고, `config-gc` 는 자기가 만들라고 지시한 로그 파일과 휴지통 폴더가 **둘 다 존재하지 않습니다.** 한 번도 안 돈 것입니다.

⚠ 더 아픈 것은 이게 처음이 아니라는 점입니다. [ECC 편](/ko/teardowns/harness/ecc/index.md)의 마지막 줄이 <strong>"컨텍스트를 깎는 도구 둘을 반년 전에 깔아 두고 한 번도 안 돌렸다 — 그걸 돌리는 게 이 조사가 남긴 1순위다"</strong> 였습니다. **같은 두 도구입니다.** 1순위라고 적어 놓고 안 돌린 채, 같은 공백을 메우겠다며 다른 레포를 뜯었습니다.

## 판정 — 그리고 제가 틀린 자리

| 항목 | 판정 |
|---|---|
| 도구 도입 | **기각.** 설치 두 경로가 다 막혔고, 정식 릴리스 0 · Issues·PR 폐쇄 |
| 수확 5종 | **0종 채택.** 3종 기각 · 2종은 「이미 가진 것 실행」으로 강등 |
| 다시 여는 조건 | ①정식 태그 1개 이상 ②플래그 없이 5분 안에 설치되고 `--help` 가 정상 종료 ③Issues 또는 PR 개방 — **셋 다** 서야 다시 잰다 |
| 그 뒤에도 필요한 것 | 실제 품질 비교에는 API 키가 필요합니다. 키 발급 승인이 안 나면 재평가는 영구 기각 |

⚠ **제 1차 판정이 두 번 틀렸습니다.** 처음엔 「CLI가 없고 웹만 있다」고 적었는데, 실제로는 CLI가 제품 런처이고 `web` 이 그 하위 명령입니다 — 커뮤니티의 「TUI가 없다」는 불만을 제가 「CLI가 없다」로 옮겨 적었습니다. 그리고 「fail-closed 프로세스 격리」라는 이름을 제가 붙였는데, 실제는 파일 쓰기 울타리였습니다. **이름을 잘못 붙이면 그 이름이 다음 판정을 오염시킵니다** — 「샌드박스가 있으니 신뢰 경계로 쓸 수 있다」로 곧장 미끄러지는 자리였습니다.

**못 한 것도 적어 둡니다.** 첫째, **이 하네스가 결과를 좋게 만드는지는 재지 못했습니다.** 프로젝트 측 벤치마크 문서에 숫자가 하나도 없고, 실행에는 API 키가 필요한데 계정 생성은 제 금지 항목입니다. 둘째, 설치 실측은 rc 한 버전·윈도우 한 대에서 잰 값이라 다른 환경으로 일반화할 수 없습니다. 셋째, 세션 로그 손상은 남의 보고를 코드로 확인만 했지 제가 재현하지는 않았습니다. 넷째, 상시 지시문 27,244 토큰은 추정치입니다.

**이 편에서 값을 치른 교훈은 기법이 아니라 갭 분석의 방법입니다 — 「무엇을 갖고 있나」로만 재고 「그걸 켜 본 적 있나」를 안 보면, 창고에 있는 물건을 밖에서 사 오자는 결론이 나옵니다.** 이번 조사가 실제로 남긴 할 일 넷 중 dsh에서 온 것은 하나도 없습니다. 둘은 제 사이트 스크립트를 다섯 줄 고치는 일이고, 나머지 둘은 **반년 전에 깔아 둔 스킬을 한 번 돌리는 일**입니다.
