Skip to content

grok v0.2.17 — 게이트를 지키던 것이 아무것도 없던 자리

Choose a tag to compare

@xzawed xzawed released this 04 Sep 08:48
· 21 commits to main since this release
29f2236

v0.2.17 — 게이트를 지키던 것이 아무것도 없던 자리

2026-09-04. 세 번째 스윕이 남긴 새 기능 4건(E1~E4) 을 오너 승인 후 전부 구현했다. 넷 다
"기능이 없다"가 아니라 "검사가 없다" 였고, 착수 전에 각 항목을 다시 실측하고 Grok으로
반증까지 시킨 뒤 done 정의를 먼저 썼다.

동작은 하나도 바뀌지 않았다. 이 릴리스가 범프인 이유는 index.ts가 쪼개져 번들 바이트가
달라졌기 때문이다 — 캐시는 버전 키다.

E1 — MCP 툴 핸들러를 아무 테스트도 실행하지 않았다

실측(착수 전). isError: result.status !== 'completed'를 delegate·plan·verify 세 반환
지점에서 isError: false로 바꿨다. tsc --noEmit 통과, 352개 테스트 전부 녹색.
isError는 MCP 클라이언트가 "이 위임이 실패했는가"를 아는 유일한 신호다. 뒤집힌 계약은
grok_error로 끝난 실행을 Claude에게 성공이라고 말한다.

원인은 핸들러가 main() 안의 익명 클로저라 호출할 방법 자체가 없었다는 것이다. Grok도
같은 결론을 냈다 — 테스트 트리에 ../src/index import도, MCP 클라이언트도, callTool도 없고,
tool-surface.test.ts는 번들을 텍스트로 스캔할 뿐이다.

한 일. 등록과 핸들러를 src/server.ts로 옮기고 index.ts는 stdio 진입점만 남겼다 —
hook-entry.ts/hook.ts가 이미 쓰던 분리다. 핸들러가 손대는 모든 부작용은 ServerDeps
지나며 기본값은 실제 구현이다. 그래서 테스트가 인메모리 전송으로 진짜 등록된 tool을
클라이언트처럼 호출한다 — 등록·스키마·핸들러 본문·반환 봉투가 한 번에 덮인다. grok 프로세스도
홈 디렉터리도 건드리지 않는다.

test/server-tools.test.ts가 고정하는 것:

  • 9개 tool이 그대로 노출되는지 (listTools)
  • delegate/plan/verify: completedisError: false, 그 외 → true
  • auth 사전 검사 실패 시 grok을 부르지 않고 즉시 중단
  • 세 tool 모두 recordDelegation을 타이밍과 함께 호출
  • plan은 plan: true, verify는 check: true를 입력에 실어 보냄
  • grok_auth_check·grok_build_statusauth.ok를, grok_clierror/timeout을,
    worktree 5개 액션은 각각 result.ok를 반영
  • worktree_path 없는 diff/apply/remove는 아무 작업도 실행하지 않고 fail closed

done 검증. 같은 뮤테이션을 다시 넣으니 이번엔 3건 실패한다. 원복 후 전부 녹색.
이 항목만으로 테스트 수 352 → 376.

E2 — 배포되는 skills/agents 프론트매터를 읽지 못하던 검사

plugin-surface.test.ts는 내부용 .claude/ 스킬 두 개에는 name 일치와 description 길이
40자 초과를 요구하면서, 실제로 배포되는 skills/grok-first-mile·skills/grok-routing·
agents/grok-worker에는 existsSync 세 줄만 걸어두고 있었다.

원인이 흥미롭다. 세 파일 모두 description: > 폴드 스칼라를 쓰는데, 기존 헬퍼의
/^([a-zA-Z0-9_-]+):\s*(.*)$/는 그 줄에서 ">" 하나만 잡고 들여쓴 본문은 버린다. 즉 기존
헬퍼로 길이 검사를 걸었어도 ">"의 길이를 재고 통과했을 것이다. Grok이 실제로 값을
측정해 description === ">" 임을 확인했다.

폴드 스칼라(>/|/>-/|-)를 이해하는 파서를 더하고, 세 파일의 name·description
검증한다. 파서 자체가 접힌 값을 펴는지도 픽스처로 고정했다 — ">"를 검사하는 가드는
아무것도 지키지 못하기 때문이다.

done 검증. grok-routing의 description을 짧게 만들면 RED.

E3 — 선언된 버전에 태그·릴리스가 있는지 아무도 보지 않았다

v0.2.12는 매니페스트 두 곳에 선언된 채 태그도 릴리스도 없이 main에 있었다. 마켓플레이스
소스가 ./이고 캐시가 버전 키라, 그 사이 설치자는 그 번호로 옛 번들을 캐시한다. 그래서
번호를 재사용하지 못하고 버렸다.

.github/workflows/ci.ymlpush/pull_request에만 걸려 있고, 태그나 릴리스를 보는 검사는
저장소 어디에도 없었다(Grok 확인).

한 일. mcp-server/scripts/check-release-tag.mjs — 선언 버전의 git 태그와 GitHub 릴리스를
모두 확인하고, 실패 시 캐시 규칙을 인용해 무엇을 하라고 말한다. release-tag-check.yml
이를 실행한다.

⚠️ 일부러 push/PR에 걸지 않았다. 버전을 선언하는 커밋은 태그보다 먼저 머지된다
(CONTRIBUTING: "머지 직후 바로 태그"). PR 시점에 검사하면 모든 릴리스 PR이 오탐으로 빨개진다.
schedule(매일) + workflow_dispatch에서는 "선언된 지 하루가 지났는데 아직 태그가 없다"가
모호하지 않다. 체크아웃은 fetch-depth: 0 — 얕은 클론에는 태그가 없어 잘못된 이유로
통과하거나 실패한다.

done 검증. 스크립트를 로컬에서 세 번 돌렸다: 0.2.16(태그+릴리스) → 통과 /
0.2.12(소급 태그는 있으나 릴리스 없음) → 실패, 정확히 역사적 사고를 잡는다 /
9.9.9 → 둘 다 없다고 실패.

E4 — marketplace.json을 파싱하는 것이 하나도 없었다

모든 설치가 이 파일을 통해 플러그인을 찾는데, 테스트도 CI도 이 파일을 열지 않았다. JSON이
깨지거나 엔트리 이름이 plugin.json과 어긋나도 전부 녹색으로 배포된다.

유효한 JSON인지, namegrok-marketplace인지, 엔트리가 정확히 하나이고 그 이름이
plugin.json의 이름과 같은지, source./인지(버전 키 캐시 규칙이 여기에 의존한다)를
고정했다.

done 검증. 마켓플레이스 이름 변경 → RED. 엔트리 이름 드리프트 → RED.

검증

  • npm ci → SDK 1.29.0 = lockfile 일치
  • npm test 379 passed / 1 skipped (v0.2.16 시점 352 — E1이 +24, E2·E4가 +3)
  • npm run typecheck, npm run build — 번들 2개 재생성
  • 뮤테이션 3종을 실제로 넣었다 뺐다 하며 RED→GREEN 확인 (E1·E2·E4)
  • check-release-tag.mjs 3케이스 실측 (E3)
  • Grok 적대적 교차검증 4건 — E1·E2·E3·E4 주장 전부 CONFIRMED, 파일 무변경

남은 것

이 레포에 열린 코드 항목은 없다. 남은 것은 사람 손이 필요한 것뿐이다 — 설치본 갱신,
GUI 수동 수락 1회, 만료 세션 실측(실계정+시간), SCAManager 토큰의 발급처 revoke 확인.
분류는 docs/09-scope-and-residuals.md.