DSH better-sidebar
간판 수치 「~325KB 코어」가 실측으로 깨졌습니다. 그런데 게이트 없는 남의 저장소를 심판하던 제 게이트가, 정작 제 저장소에서는 한 번도 커밋된 적이 없었습니다.
확장 베이스로 파는 물건인데, 간판 수치가 실측과 어긋났습니다. DSH better-sidebar 는 DeepSeek Harness 라는 에이전트 CLI 의 웹 모드에 붙어 파일 탐색기·에디터·터미널·Git·프리뷰를 한 화면에 얹는 npm 플러그인입니다. 별 2,575개, 저장소를 만든 지 15일 만에 커밋 250건. README 첫 줄이 자랑하는 건 「기동 시 ~325KB 코어만 내려받는다」인데, npm 산출물을 직접 재보니 발행된 다섯 버전 중 그 값이었던 것은 하나도 없었습니다 — 0.12.1 이 405KB, 9일 뒤 0.15.0 이 585KB(+45%). 원인은 단순합니다. CI 에 크기 게이트가 없습니다. 그런데 이 편이 남을 이유는 그쪽이 아닙니다 — 게이트가 없어 수치가 새어 나간 남의 저장소를 심판하던 그 게이트가, 정작 제 저장소에서는 버전관리 밖에 있었습니다.
확장 베이스는 사이드바가 아니라, 그 사이드바를 남에게 내주는 두 개의 문이다
한 패키지가 서버 반쪽과 브라우저 반쪽으로 갈립니다. 서버 반쪽은 /sidebar/api/* JSON 디스패치 테이블(파일 트리·읽기·쓰기·검색, Git 12종, 서브에이전트 현황)과 미디어·HTML 프리뷰·지연 청크 라우트, 터미널 WebSocket 을 등록합니다. 요청마다 sessionId 로 작업 디렉터리를 스코프하지만, 세션이 아직 안 붙은 첫 페인트를 위해 프로세스 cwd 까지 내려가는 3단 폴백이 있어 「전 기능이 세션 단위로 격리된다」는 정확한 서술이 아닙니다. 브라우저 반쪽은 우측·하단 두 워크벤치를 포털로 띄우고 각 패널을 재귀 split 트리로 쪼갠 뒤, 레이아웃을 세션별 키로 localStorage 에 저장했다가 복원할 때 구조 검증·뷰포트 클램프·중복 pane id 재발급을 거칩니다. 터미널은 흉내가 아니라 실제 셸이고, 켜면 모델에게 8종 도구가 주입돼 에이전트가 장수 PTY 를 부립니다.
| 항목 | 실측 (2026-08-22) |
|---|---|
| 규모 | 227파일 2.4MB · 테스트 82파일 672KB · 설계 문서 21편 |
| 의존 | 런타임 27종 · peer 18종 중 15종이 프리릴리스 |
| 활동 | 08-07 생성 → 08-21 푸시. 커밋 250건 · 릴리스 17건 / 11일 |
| 버스팩터 | 1 — 1위 217커밋(87%), 2위 7커밋 |
| PR | 병합 68 · 종료 35 · 열림 78 (열린 PR 이 역대 머지 총량을 넘었다) |
여기서 값을 치른 건 「기여자 22명」을 분산으로 읽을 뻔한 것입니다. 19명이 1~2커밋이고 대부분 자기 플러그인을 추천 목록에 올리는 PR 입니다. 기여자 수는 코어 개발의 분산과 무관했습니다.
제일 잘한 것은 자기 내장 기능마저 외부와 같은 문으로 밀어 넣은 것이다
| 기법 | 무엇 |
|---|---|
| 자기 도그푸딩 | 내장 7 tab · 6 viewer 가 외부와 똑같은 registerTab/registerFileViewer 를 통과한다. 주석이 이를 “eating its own dogfood” 라 부른다 |
| detect → exts 2단 매칭 | 뷰어 선택은 내용 스니핑 먼저, 확장자가 그다음. 만능 매치가 detect 를 가지면 ’스니핑 전용’으로 격하돼 양보한다 — 매직넘버 뷰어가 모든 파일을 먼저 삼키는 걸 구조가 막는다 |
| dedupeKey | 하드코딩돼 있던 탭 열기 전략 3종(단일·경로별·id별)을 함수 필드 하나로 흡수. ’생성’과 ’포커스’를 갈라 콜백을 따로 쏜다 |
| 빌드 순도 게이트 | 화이트리스트 밖 패키지를 값으로 import 하면 빌드가 깨진다. 문구가 “collaborate through cordis services” — 협업 경로를 하나로 강제한다 |
| 등급 강등 마운트 | 네이티브 의존 로드 실패가 서버를 죽이지 않는다. 강등된 채 마운트되고 모델용 도구는 아예 미등록, 사람에겐 붙여넣을 수리 명령을 준다 |
| 이중 마운트 자동 양보 | 같은 패키지가 두 엔트리로 뜨면 라우트 중복으로 부팅이 통째 실패한다. 런타임 싱글턴이 아니라 로더 표현식으로 자기 행을 스스로 끈다 |
| 사이드 대화 시드 | 자식 세션에 부모 로그를 넘길 때 진행 중이던 턴을 합성 이벤트로 정직하게 닫고, “참조 컨텍스트일 뿐 현재 과업이 아니다” 를 못박는다. 테스트가 그 문장을 고정한다 |
배운 건 기법이 아니라 「그 규약을 어디에 거는가」입니다. 순도 게이트는 협업 규약을 사람 주석이 아니라 빌드에 걸었습니다. 뒤에서 보겠지만 제 쪽의 같은 규약은 주석에 걸려 있었고, 실제로 한 번 갈라졌습니다.
간판 셋이 다 안 버텼고, 그중 하나는 이슈 트래커에도 없는 결함이었다
- 「~325KB 코어」 — 깨졌습니다. tarball 다섯을 직접 재니 405 → 424 → 459 → 482 → 585KB. 미니파이도 안 된 12,283줄이라 「미니파이 기준」이라는 변명도 안 통합니다. 같은 저장소의 청크 로더 주석은 “only the ~1MB core bundle” 이라 세 번째 숫자를 내놓습니다. 반면 「7MB mermaid 청크」는 실측 6,996,658 B 로 정확히 맞았습니다 — 저자가 수치를 못 재는 게 아니라, 게이트가 없는 수치만 갱신되지 않았습니다.
- 「내장과 서드파티가 완전 대등」 — 깨졌습니다. 등록 경로가 같은 건 사실이지만 외부 플러그인은 패널을 펼칠 수 없습니다. 서비스에 확장·토글 메서드가 없어 외부 탭은 열려도 접힌 패널 뒤에 숨고, 남는 수단이 DOM 해킹뿐이라는 오픈 이슈가 살아 있습니다.
- 「/api 와 동일한 신뢰 울타리」 — 함수는 같고 적용 범위가 다릅니다. 상류는 그 판정을 특권 메서드에만 걸고 이 플러그인은 전 경로에 겁니다. 그래서 Chrome 이 포트 없는 Origin 을 보내는
127.0.0.1접속에서 사이드바 전 라우트가 403 인데 호스트 쪽 대화는 멀쩡히 돕니다. 과소차단이 아니라 과차단입니다. - HTML 프리뷰 — URL 은 맞는데 헤더가 자산을 죽입니다. 상대 참조가 세션 스코프를 잃지 않도록 쿼리 대신 경로에 전부 인코딩한 설계는 훌륭한데, 미디어 타입 표에
.css도.js도 없어 전부octet-stream으로 나가고 같은 라우트가nosniff를 붙입니다. ⚠ 테스트가 착시를 만듭니다 — URL 디코딩만 단언하고 content-type 은 한 줄도 보지 않습니다. - 이슈에도 없는 결함 — 파일 쓰기에 낙관적 동시성 검사가 없습니다. 저장 API 가 받는 건 경로와 내용뿐, mtime 도 해시도 없습니다. 여기에 저장소 스스로 인정한 「파일 watcher 없음」이 겹칩니다. 탭을 열어둔 채 에이전트가 그 파일을 재작성하면, 사람이 저장하는 순간 에이전트의 최신 내용이 경고 없이 사라집니다 — 이 제품의 존재 이유인 시나리오에서 고전적 lost update 입니다.
- 「3플랫폼 지원」 — CI 두 잡이 전부 ubuntu 단일입니다. 하필 미해결 P0 중 가장 아픈 것이 Windows 에 몰려 있습니다. 설치만 하면 기존 세션 재개가 전부 실패하고, 제거하면 즉시 복구되며, 메인테이너는 mac 에서 재현하지 못합니다.
버틴 것도 적어 둡니다. 「진짜 셸」은 사실이고, 「README 가 아직 내장 Office 프리뷰를 광고한다」는 외부 비판은 이미 수정돼 더는 성립하지 않습니다 — 외부 비판도 낡습니다. 그리고 이 절에서 값을 치른 건 「주석 밀도」를 성숙도로 읽은 것입니다. 이 저장소는 ’왜 이렇게 안 했나’를 적는 드문 주석 문화를 갖고 있어 1차에 그걸 성숙도 근거로 세웠는데, 존재하지 않는 탭 이름을 가리키는 주석과 상류 재라이선스를 못 따라간 표기가 HEAD 에 살아 있었습니다. 검증되지 않은 고밀도 주석은 틀린 문서와 값이 같습니다.
도입 판정은 검토가 아니라 측정 세 줄로 끝났다
where dsh 결과 없음. ~/.dsh 없음. 전역 npm 패키지 9종에 그 생태계 0건. peer 18종 중 15종이 그 생태계인 플러그인이라 설치 경로 자체가 존재하지 않습니다. 「지금은 안 맞는다」가 아니라 「호스트가 없다」입니다. 그래서 남는 건 기법 이식인데, 후보 넷 중 실제로 남은 건 하나였습니다.
- 번들 크기 게이트 → 이득 0. 대상의 최대 결함이 하필 제가 이미 메운 자리였습니다.
scripts/check-weight.mjs의 상한 9종은 전부dist로 판정하고 상한마다 유도 근거가 주석에 붙어 있으며,scripts/deploy.mjs는 링크·앵커 → 가벼움 → 용어사전 → 마커 4단 fail-closed 입니다. - dedupeKey → 이득 0. TC 파이프라인이 소유마커 하나로 열기 정책 3분기를 이미 합니다 — 신규 생성 / 우리 소유면 멱등 재기록 / 남의 것이면 접미사로 양보.
- 강등 마운트 → 기각. 붙이려던 로컬 모델 MCP 서버가 어떤 설정에도 등록돼 있지 않았습니다. 서버 이름·도구 이름·슬래시 커맨드가 세 갈래로 어긋나 되살려도 안 붙습니다. 적용처가 없는 수확은 수확이 아닙니다.
- 이중 마운트 자동 양보 → 유일한 수확. 대응물이 있는지 Loki 를 열었더니 lockfile·pidfile·싱글턴 가드가 0건이었습니다. 다만 ⚠ 제가 짚은 위치가 틀렸습니다 — 5분마다 도는 워치독 경로는 이미
ensure()가 막고 있었고, 안 막힌 건 로그인 시작프로그램 항목이gateway ensure가 아니라 모듈 직행이라는 점이었습니다. 최소 수정은 새 선점 프로토콜이 아니라 한 줄입니다.
⚠ 그리고 제가 적은 검증 명령이 한 번도 실행해 보지 않은 것이었습니다. “프로세스 개수가 1인지 확인한다” 라고 썼는데, venv 런처 스텁 때문에 건강한 단일 워커도 항상 2개로 보입니다(부모는 스레드 1·소켓 0, 실제 연결은 자식이 쥡니다). 그 방법으로 자기 양보를 구현하면 정상 워커가 매 부팅 스스로 죽습니다. 인용한 수치가 틀린 것보다 지어낸 검증 절차가 더 위험합니다 — 전자는 다시 재면 되지만, 후자는 통과했다는 착각을 남깁니다.
남을 심판한 잣대가 커밋 밖에 있었다
git ls-files scripts/lib/ 가 빈 결과를 냅니다. 판정 게이트 5종(check-links.mjs·check-weight.mjs·check-glossary.mjs·collect-ai-issue.mjs·lib/markers.mjs)이 전부 미추적이고, git check-ignore 는 무시 대상이 아니라고 답합니다 — 일부러 제외한 게 아니라 한 번도 커밋된 적이 없습니다. markers.mjs 헤더는 스스로 「이 파일이 정본이다」라 선언하고 재발 이력까지 적어 뒀는데, 그 파일의 git log 는 0건입니다. 정본이라 선언한 파일에 히스토리가 없습니다.
추적되는 deploy.mjs 는 그 다섯을 하드 의존해서 신선한 클론에서는 첫 줄 import 가 즉사합니다. 유출은 안 납니다 — fail-closed 라 배포가 멈출 뿐입니다. 대신 대상의 수확 절반을 「나는 이미 더 나은 걸 갖고 있다」로 죽인 그 근거가, 저장소에서는 재현되지 않습니다. 대상의 병은 게이트 부재였고 제 병은 게이트의 무근거성인데, 뒤가 더 비쌉니다. 없는 게이트는 없다는 걸 누구나 알지만, 있는데 커밋 밖에 있는 게이트는 있다고 믿게 만듭니다.
판정 — 그리고 못 한 것
| 항목 | 판정 | 사유 · 재개 조건 |
|---|---|---|
| 제품 도입 | ⛔ 기각 | 호스트가 없다. 재개 = 이 하네스를 실제로 쓰게 될 때. 그때도 먼저 볼 것은 미해결 P0 셋 |
| 크기 게이트 · dedupeKey | 이득 0 | 이미 더 나은 형태로 있다. ⛔ 이 축으로는 다시 열지 않는다 |
| 강등 마운트 | 기각 | 적용처가 미등록 유물이었다. 재개 = 그 서버를 실제로 배선할 때 |
| 부팅 시 자기 양보 | 채택 | 시작프로그램 한 줄 + 재시작 경로용 원자적 선점. 검증은 프로세스 개수가 아니라 하트비트 레코드로 |
| 경계 문장 이식 | 보류 | 단계별 적대검증이 이미 그 역할을 한다. 실효는 「검증 명령은 판정 전에 한 번 실행하고 출력을 붙인다」 쪽 |
| 게이트 5종 커밋 | 1순위 | 비용 거의 0. 검증 = 임시 클론에서 배포 체인이 실제로 통과하는지 |
못 한 것을 적어 둡니다. ①이 플러그인을 한 번도 실행해 보지 못했습니다 — 호스트가 없어 설치가 안 되므로 판정은 전부 소스·npm 산출물·이슈 트래커 실측이고 화면을 본 적이 없습니다. 번들 크기는 확실하지만 「기동 시 실제로 무엇이 내려가는가」는 코드로만 확인했습니다. ②외부 설치 리그의 실패 보고는 보강 증거로만 썼습니다 — 헤드리스 부팅으로 재는 리그인데 이 플러그인은 웹 프로필 전용이라 실패가 리그 아티팩트일 수 있습니다. ③생태 플러그인 26건은 세기만 했습니다. 등재 조건이 자기신고식 태그 하나라 그 수는 활동성의 증거가 아닙니다. ④채택한 한 건은 아직 안 고쳤습니다. 이 글은 판정까지입니다.
이 편의 관통선은 이것입니다 — 남의 수치를 의심하는 도구부터, 내 저장소에 실려 있는지 본다. 대상을 심판하는 데 쓴 잣대가 내 쪽에서 재현되지 않으면, 그 심판은 판정이 아니라 인상입니다.