뜯어본 생각공개

DSH better-sidebar

간판 수치 「~325KB 코어」가 실측으로 깨졌습니다. 그런데 게이트 없는 남의 저장소를 심판하던 제 게이트가, 정작 제 저장소에서는 한 번도 커밋된 적이 없었습니다.

AI 코딩 도구 화면에 파일 탐색기·편집기·터미널을 한꺼번에 얹어 주는 부가 프로그램입니다. 간판 숫자가 실측과 안 맞았습니다. 그런데 이 편이 남을 이유는 그쪽이 아닙니다 — 남을 심판하던 «제 검사 장치»가 정작 제 저장소에 한 번도 커밋된 적이 없었습니다.

npm 패키지 하나가 두 반쪽으로 갈린다 — 서버 라우트와 브라우저 워크벤치 host 반쪽 — /sidebar/* 라우트 fs · git · pty · HTML 프리뷰 · 지연 청크 요청마다 sessionId → 세션 cwd 로 스코프 client 반쪽 — 포털 워크벤치 우측 · 하단 패널을 재귀 split 트리로 쪼갠다 레이아웃은 세션별로 localStorage 에 저장 확장 지점은 딱 둘 — 그리고 내장 기능도 같은 문으로 들어온다 registerTab registerFileViewer 내장 7 tab · 6 viewer 도 통과 ⇒ 확장 베이스로 파는 물건이라 간판 수치와 「능력 대등」 주장이 곧 제품이다 그 둘을 재봤더니 하나는 실측과 어긋났고 하나는 자기 소스 주석이 반박한다
탭 개수를 세는 것으로는 이 물건이 안 보인다. 내장까지 같은 문으로 밀어 넣은 것이 정체다.

이 물건이 뭔가요

AI 코딩 도구를 웹 화면으로 쓸 때, 원래는 대화창만 있습니다. 파일을 보려면 다른 프로그램을 켜야 합니다.

이 부가 프로그램을 깔면 한 화면 안에 다 들어옵니다 — 파일 목록, 편집기, 터미널, 변경 이력, 미리보기.

만들어진 지 15일에 커밋 250건, 「좋아요」 2,575개입니다.

제일 잘한 것 — 「자기 기능도 남과 같은 문으로 들어오게 했다」

이 창고에서 제일 배울 만했던 설계입니다.

보통 이런 프로그램은 자기 기능은 특별 통로로 넣고, 남이 만든 확장은 좁은 문으로 받습니다. 그러면 남이 만든 건 자기 것만큼 잘 안 됩니다.

이 창고는 자기 내장 기능 13개도 외부 확장과 «똑같은 문»을 통과시켰습니다. 주석에 이렇게 적혀 있습니다 — 「자기가 만든 사료를 자기가 먹는다.」

이러면 문이 좁으면 자기가 먼저 불편합니다. 그래서 문이 안 좁아집니다.

또 하나 — 「부품이 고장 나도 전체가 안 죽는다」

두 번째로 좋았던 것입니다.

어떤 부품은 컴퓨터마다 깔릴 수도 안 깔릴 수도 있습니다. 보통은 그게 없으면 프로그램 전체가 안 뜹니다.

이 창고는 이렇게 처리합니다.

상황 보통 이 창고
부품 로드 실패 전체 중단 등급을 낮춰서 뜸
AI에게 주는 도구 — 그 기능만 «아예 안 알려 줌»
사람에게는 오류 메시지 붙여넣을 수리 명령을 줌

두 번째 줄이 좋습니다. 안 되는 기능을 AI에게 있다고 알려 주면, AI가 계속 시도하다 실패합니다. 아예 없는 것으로 알려 주면 AI가 다른 길을 찾습니다.

그런데 간판 숫자가 안 맞았습니다

설명서 첫 줄의 자랑이 「시작할 때 약 325KB만 내려받는다」입니다.

발행된 판 다섯 개를 직접 받아서 재 봤습니다.

판 실제 크기
첫 번째 405KB
… 424 → 459 → 482
9일 뒤 585KB (+45%)

325KB였던 적이 한 번도 없습니다.

「압축 기준이겠지」도 안 통합니다. 압축도 안 된 12,283줄이었습니다.

더 웃긴 건, 같은 창고의 다른 주석이 세 번째 숫자를 말한다는 것입니다.

그런데 다른 숫자 하나는 정확히 맞았습니다

여기가 이 편에서 제일 재미있는 대목입니다.

같은 설명서에 「어떤 부품은 7MB」라는 숫자도 있었습니다. 재 보니 정확히 맞았습니다.

즉 이 사람이 숫자를 못 재는 게 아닙니다.

차이가 뭐냐면 — 7MB 쪽은 자동 검사가 걸려 있고, 325KB 쪽은 안 걸려 있었습니다.

게이트가 없는 숫자만 낡습니다. 사람이 성실해서 갱신되는 게 아니라, 기계가 울어서 갱신됩니다.

이슈 목록에도 없는 결함을 하나 찾았습니다

읽다가 발견한 것입니다. 아무도 신고하지 않았습니다.

상황: 제가 파일을 편집기에 열어 뒀습니다. 그동안 AI가 같은 파일을 고쳐 씁니다.

그리고 제가 저장을 누릅니다.

무슨 일이 일어나나
제 편집기에는 열었을 때의 옛 내용이 있음
저장하면 AI가 방금 쓴 내용이 통째로 사라짐
경고가 없음

왜 이렇게 되냐면, 저장할 때 「내가 연 뒤에 누가 고쳤나」를 확인하지 않기 때문입니다. 경로와 내용만 받고 그냥 덮어씁니다.

그리고 파일이 바뀐 걸 감지하는 기능도 없다고 창고가 스스로 인정합니다. 둘이 겹치면 위 사고가 납니다.

이게 아픈 이유는 — 그 상황이 바로 이 제품의 존재 이유이기 때문입니다. 사람과 AI가 같은 파일을 같이 보라고 만든 물건입니다.

테스트가 착시를 만든 자리도 하나

미리보기 기능에 문제가 있었습니다. 주소 처리는 잘돼 있는데, 파일 종류를 알려 주는 표에 두 가지가 빠져 있어서 화면 꾸밈이 전부 안 먹습니다.

그런데 테스트는 통과합니다.

테스트가 주소 처리만 확인하고, 파일 종류는 한 줄도 안 봅니다. 그래서 초록불인데 화면은 깨져 있습니다.

→ 테스트가 있다는 것과, 그 테스트가 이 결함을 본다는 것은 다릅니다.

도입 판정은 검토가 아니라 측정 세 줄로 끝났습니다

이 물건을 쓸지 고민할 필요가 없었습니다. 세 줄 쳐 보고 끝났습니다.

  • 이 부가 프로그램이 붙을 본체 프로그램이 제 컴퓨터에 없음
  • 그 설정 폴더도 없음
  • 제 전역 설치 목록에 그 계열 0건

「지금은 안 맞는다」가 아니라 「붙일 본체가 없다」입니다.

그런데 여기서 제 쪽이 나왔습니다

이 편의 본론입니다.

저는 위에서 「자동 검사가 없어서 숫자가 낡았다」고 남을 심판했습니다.

그 심판에 쓴 자, 즉 제 검사 장치들을 확인해 봤습니다.

제 저장소에 한 번도 커밋된 적이 없었습니다.

검사 장치 다섯 개가 전부 버전 관리 밖에 있었습니다. 일부러 제외한 것도 아니었습니다 — 제외 목록에도 없었습니다. 그냥 한 번도 올라간 적이 없었습니다.

제일 아픈 건 이겁니다. 그중 한 파일의 머리말에 「이 파일이 정본이다」라고 제가 적어 뒀고, 재발 이력까지 적어 뒀습니다.

그런데 그 파일의 변경 이력이 0건입니다.

정본이라고 선언한 파일에 히스토리가 없었습니다.

즉 그 파일이 지워지면 복구할 방법이 없고, 누가 언제 왜 고쳤는지도 알 수 없습니다.

판정

항목 판정 왜
제품 도입 기각 붙일 본체가 없음
크기 검사 장치 이득 0 이미 더 나은 형태로 있음
고장 시 등급 강등 기각 적용할 자리가 안 쓰는 유물이었음
부팅 시 자기 양보 채택 같은 게 두 번 뜨면 자기가 스스로 물러나는 방식

마지막 줄만 가져왔습니다. 그리고 확인 방법을 「프로세스가 몇 개인지」가 아니라 「살아 있다는 기록이 남는지」로 잡았습니다 — 개수만 세면 좀비가 된 것도 하나로 세어집니다.

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

확장 베이스로 파는 물건인데, 간판 수치가 실측과 어긋났습니다. 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 에 살아 있었습니다. 검증되지 않은 고밀도 주석은 틀린 문서와 값이 같습니다.

코어 번들 실측 — npm tarball 을 직접 열어 쟀다 (2026-08-22) 대상 주장 ~325KB 문서 목표치 · 실재하지 않음 0.12.1 (08-13) 405KB 0.12.3 (08-16) 424KB 0.13.1 (08-18) 459KB 0.14.0 (08-19) 482KB 0.15.0 (08-21) 585KB ⇒ 다섯 버전 중 325KB 였던 것은 하나도 없다 — 9일 만에 +45%, gzip 도 +47% 같은 저장소가 세 숫자를 동시에 든다 — README 325KB · 로더 주석 ~1MB · 실측 585KB 원인은 CI 에 크기 게이트가 없어서다. 게이트가 있는 「7MB mermaid 청크」는 정확히 맞았다 ⇒ 그 잣대로 남을 심판했는데, 내 게이트 5종은 저장소에 커밋된 적이 없었다
수치가 틀렸다기보다 아무도 지키지 않는다. 크기 게이트 한 줄이 없는 자리에서 9일에 45%가 샜다.

도입 판정은 검토가 아니라 측정 세 줄로 끝났다

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건은 세기만 했습니다. 등재 조건이 자기신고식 태그 하나라 그 수는 활동성의 증거가 아닙니다. ④채택한 한 건은 아직 안 고쳤습니다. 이 글은 판정까지입니다.

이 편의 관통선은 이것입니다 — 남의 수치를 의심하는 도구부터, 내 저장소에 실려 있는지 본다. 대상을 심판하는 데 쓴 잣대가 내 쪽에서 재현되지 않으면, 그 심판은 판정이 아니라 인상입니다.