00발단 · 스킬 하나가 던진 질문 실사례
impeccable이라는 디자인 스킬을 평가하다가, 좋은 스킬을 "어디까지 켜둘 것인가"라는 더 오래된 문제에 도착했다. 이 글은 그 문제의 커뮤니티 표준 답안 세 갈래와, keystone이 그중 어디에 서 있는지의 기록이다.
사건의 순서
1. pbakaus/impeccable(⭐54.9k, Apache-2.0)을 skill-judge 8차원으로 평가: 약 110/120, A등급. craft-floor의 금지 목록과 critique의 격리 서브에이전트 프로토콜은 keystone의 적대 검증 철학과 거의 동형이었다.
2. 문제는 품질이 아니라 라우팅이었다. description이 UI 어휘 전체를 흡수하도록 쓰여 있어, 이미 있는 frontend-design · omd 스위트 · design-loop과 자동 발동이 상시 충돌한다. 스킬이 좋다는 것과 카탈로그가 좋아진다는 것은 다른 명제다.
3. "활성/비활성으로 관리하면 되지 않나"라는 질문에서 스코프 구조를 파봤더니, user 스코프는 머신-로컬이었다. 머신이 세 대면 세 번 꺼야 하고, 더 나쁘게는, 이 환경에서는 CLI로 꺼봤자 다음 keystone apply가 레이어 시드로 되돌린다. runtime-preserve가 보존하는 키는 model 하나뿐이라는 것을 apply.sh 소스에서 확인했다.
4. 그래서 정석 경로로 갔다: 레이어 소스 수정(3줄) → PR #454 → 병합 → apply → 런타임 실측(impeccable=false, 텔레메트리 opt-out env 반영, model 키 보존) → 플러그인 3.1.1→4.0.4 업데이트. 이 한 바퀴가 곧 이 글의 주제다.
"다른 사용자들도 머신이 여러 대일 때 이렇게(git 소스 → 머신별 apply) 하나? 아니면 프로젝트 스코프로만 버티나?" 답은 "둘 다 존재하고, 층이 나뉜다"이다. §02가 그 3층이고, §03이 keystone의 좌표, §04가 그 표준 도구인 chezmoi의 해부다.
01스코프 해부 · 공식 레버 세 개
Claude Code가 공식으로 제공하는 활성/비활성 수단은 세 개다. 전부 이 세션에서 CLI help·공식 문서·로컬 설정 파일로 직접 확인했다.
| 레버 | 동작 | 전파 범위 |
|---|---|---|
disable-model-invocation: trueSKILL.md frontmatter |
자동 라우팅에서 완전 제외. description이 컨텍스트에 아예 실리지 않아 상시 토큰도 절약되고, /skill-name 명시 호출은 그대로 동작한다. "좋은데 시끄러운 스킬"의 정답. |
스킬 파일이 가는 곳 어디든단, 플러그인 스킬은 캐시 사본이라 직접 수정 시 업데이트에 덮인다 |
enabledPlugins + claude plugin enable|disable -s user|project|local |
플러그인 통째 on/off. -s project는 repo의 settings.json에 기록되므로 git으로 전파: 머신 문제가 애초에 없다. |
user = 머신-로컬 project = repo 따라 전머신 |
| 카탈로그 아카이브keystone 자체 레인 (#401) | 안 쓰는 스킬을 아카이브 디렉토리로 이동해 카탈로그에서 제거. 238→180개 실적. 명시 호출도 사라지는 완전 제거라는 점이 위 두 레버와 다르다. | keystone apply가 가는 곳 |
keystone은 enabledPlugins를 레이어 소스(layers/10-os-macos/settings.json)에서 시드로 관리한다. 로컬에서 claude plugin disable을 실행해도 다음 apply가 시드 값으로 되돌린다. apply.sh의 runtime-preserve 목록에 있는 키는 model 하나뿐(apply.sh:559)이라는 것을 소스에서 확인했다. "런타임에서 조작한 설정 키는 apply 때 증발한다"는 알려진 사고 계열의 정확한 재현 조건이다.
따라서 이 환경의 유일한 지속 경로는 소스 수정 → PR → apply다. 우회처럼 보이는 CLI 한 줄이 실제로는 다음 apply까지만 사는 일회용이다.
이번에 실제로 흘린 경로
# 1. 레이어 소스 수정 (worktree, 3줄 diff) "impeccable@impeccable": true → false env["IMPECCABLE_NO_TELEMETRY"] = "1" # concept-seed 선택 핑 opt-out # 2. PR #454 → squash 병합 → 이 Mac apply ./apply.sh --profile personal-macos # → 런타임 실측: false / "1" / model 보존 # 3. 플러그인 갱신 (설치 캐시는 머신별) claude plugin update impeccable@impeccable # 3.1.1 → 4.0.4 # 4. 디자인 작업 repo에서만 켜기 (git으로 전파되므로 머신 무관 .claude/settings.json → { "enabledPlugins": { "impeccable@impeccable": true } }
4번은 대상 프로젝트를 정하는 순간 커밋 한 번으로 끝난다. 맥 두 대는 각자 다음 apply/sync에서 1~3이 자동 정합되고, Windows 레이어에는 impeccable 항목이 원래 없어 적용 대상이 아니다.
02커뮤니티 실태 · 3층 구조 2026-08 검색 실측
"멀티머신에서 ~/.claude를 어떻게 관리하나"를 2026년 8월 기준으로 조사하면 답이 세 층으로 갈린다. 검색에 잡히는 가이드가 전부 2층에 몰려 있다는 사실 자체가 신호다. user 스코프를 동기화하려는 수요는 실재하고, 패턴은 이미 표준화됐다.
동기화 안 함,
프로젝트 스코프로 회피
팀/repo 공유 설정은 .claude/settings.json을 git에 커밋하는 게 공식 경로다. "repo가 따라다니니 머신은 상관없다"로 끝내고, user 스코프 드리프트는 그냥 수용한다.
- 도구 0개, 학습 비용 0
- 개인 설정(플러그인·모델·훅)은 머신마다 어긋난 채 방치
- 머신이 늘수록 "이 맥에선 왜 안 되지"가 누적
~/.claude를
dotfiles로 정본화
user 스코프를 "동기화되는 git 정본"으로 만든다. 변형이 넷: chezmoi(머신별 템플릿+암호화), plain git+셸 래퍼(열 때 pull, 닫을 때 push), NAS symlink(동기화 데몬 없이 즉시 공유), 전용 CLI(age 암호화 동기화 도구).
- 검색 상위 가이드 8건이 전부 이 계열
- 공통 교훈: ~/.claude 대부분은 머신 고유물(세션 로그·캐시·텔레메트리)이라 전체 동기화가 아니라 선별 동기화가 정답
- 시크릿은 age 선별 암호화로 분리
managed settings를
MDM으로 배포
조직이 managed-settings.json을 MDM/배포 도구로 전 머신에 강제한다. 개인 취향이 아니라 fleet 정책(권한·모델·보안 게이트)이 목적이고, 사용자 설정보다 우선순위가 높다.
- 개인 멀티머신 문제와는 목적이 다름
- 단, "소스 하나 → 머신 N대 강제 배포"라는 뼈대는 2층·keystone과 동형
2층 변형 비교 · 무엇이 갈리나
| 변형 | 머신별 분기 | 시크릿 | 동기화 트리거 | 실패 모드 |
|---|---|---|---|---|
| chezmoi | hostname 템플릿 내장 | age 선별 암호화 내장 | autoPush + SessionStart 훅 pull | 템플릿 학습 곡선 |
| plain git | 없음 · .gitignore 제외만 | .gitignore 의존 수동 | 셸 래퍼 (열 때 pull / 닫을 때 push) | 공개 repo에 시크릿 유출 사고 |
| NAS symlink | 없음 · 전 머신 동일 강제 | 로컬 유지 (링크 제외) | 없음 · 즉시 공유 | NAS 죽으면 전 머신 마비, 충돌 해소 없음 |
| 전용 CLI | 제한적 | age 내장 내장 | 수동 sync 명령 | 도구 자체가 단일 메인테이너 |
"유저 스코프를 프로젝트 스코프로만 대체"가 표준인 것은 아니다. 팀에 강제하면 안 되는 개인 설정: 플러그인 on/off, 모델, 훅, 키바인딩은 repo에 커밋할 수 없어서 user 스코프에 남는다. 멀티머신 사용자는 그걸 dotfiles 계열로 동기화하는 것이 관행이고, keystone은 그 관행의 자체 구현이다.
03keystone의 위치 · 패턴은 주류, 무게는 예외
keystone은 2층에 속한다. chezmoi가 개념으로 제공하는 것을 자체 스크립트로 구현한 형태라 개념 대응이 거의 1:1로 성립하고, 표준에서 벗어난 것은 방식이 아니라 짊어진 무게다.
| chezmoi 개념 | keystone 대응물 | 비고 |
|---|---|---|
| source state (git repo) | keystone-hub/layers/ | 둘 다 "git 소스가 정본, 홈 디렉토리는 산출물" |
| hostname/OS 템플릿 분기 | 레이어 × 프로파일 4종00-universal + 10-os-{mac,win} + 20-company-{personal,bs} | keystone 쪽이 한 축 더 많다. OS만이 아니라 소속(개인/회사)까지 분기 |
chezmoi apply | ./apply.sh --profile <name> | 선언 상태를 머신에 투영 |
chezmoi doctor / diff | keystone-doctor.py + manifest 드리프트 검출 | keystone은 diverged 파일을 fail-closed로 보존하는 쪽이 더 보수적 |
run_once_ 스크립트 | OS 레이어 post-apply hook예: 플러그인 경로 OS-native 변환 | 커뮤니티가 나중에 도달한 교훈(경로 변환·머신 고유물 제외)을 keystone은 사고를 겪고 규칙으로 선반영 |
| age 선별 암호화 | settings.local.json 수동 분리 공백 | keystone에 없는 유일한 chezmoi 부품 · §04 채굴 후보 |
동형이 아닌 지점 · keystone이 더 무겁게 지는 것
adapter export. chezmoi는 파일을 배포할 뿐이지만, keystone은 같은 소스에서 프로젝트별 CLAUDE.md · AGENTS.md · .cursor/rules까지 생성한다. 멀티 CLI(Claude/Codex/Cursor) 요구는 chezmoi의 설계 범위 밖이다.
runtime-preserve의 역방향 문제. chezmoi는 "소스가 항상 이긴다"로 단순하게 끝나지만, keystone은 라이브 세션이 런타임 설정을 계속 고쳐 쓰는 환경이라 "어떤 키는 런타임이 이겨야 한다"는 예외(현재 model 1개)를 관리해야 한다. 이 비대칭이 이번 impeccable 건에서 확인한 함정의 뿌리다. 예외 목록에 없는 키는 전부 apply가 이긴다.
단일 사용자 기준으로는 맞는 지적이다. chezmoi 설정 수십 줄이면 될 일을 apply.sh + doctor + adapter export로 하고 있고, 그 유지비는 실제로 발생해 왔다(apply가 런타임 키를 증발시킨 사고, diverged 처리 사고 등). 이 비용이 정당화되는 근거는 두 가지뿐이다: ① 개인/회사 × OS 4프로파일 분리는 chezmoi 템플릿으로도 어차피 직접 짜야 하는 로직이고, ② 멀티 CLI adapter export는 대체재가 없다. 이 두 요구가 사라지면 keystone의 설정 배포 부분은 chezmoi로 대체하는 게 맞다.
04chezmoi · 이점과 사용법
2층의 사실상 표준 도구. 한 문장으로: "홈 디렉토리의 원하는 상태를 git repo에 선언해 두면, 어느 머신에서든 chezmoi apply 한 번으로 그 상태가 된다"는 선언형 dotfiles 관리자다. Go 단일 바이너리라 설치 의존성이 없다.
이점 다섯 · 왜 plain git이 아니라 chezmoi인가
| 이점 | 내용 |
|---|---|
| 1. 머신별 템플릿 | 같은 파일을 머신마다 다르게 렌더링한다. settings.json을 템플릿으로 만들면 "회사 맥에서만 이 플러그인 on" 같은 분기가 파일 하나 안에서 선언된다. plain git은 이게 불가능해서 머신별 브랜치나 수동 편집으로 새는 지점. |
| 2. age 선별 암호화 | 시크릿이 든 파일만 골라 암호화해 커밋한다. repo는 공개해도 되고, 새 머신에서는 개인키만 옮기면 apply가 자동 복호화한다. "공개 dotfiles + 비밀 파일 몇 개" 구조의 정석. |
| 3. 선언형 + 안전한 적용 | chezmoi diff로 적용 전 차이를 보고, apply는 소스 상태로 수렴시킨다. "지금 이 머신이 선언과 얼마나 다른가"가 항상 한 명령이다. |
| 4. 스크립트 훅 | run_once_·run_onchange_ 스크립트로 "적용 후 한 번 실행"(도구 설치, 경로 수리)을 선언에 포함시킨다. |
| 5. 자동 동기화 루프 | autoPush(커밋 즉시 push) + 세션 시작 훅에서 pull. Claude Code 커뮤니티 gist들이 쓰는 패턴. 머신 간 전파가 "다음에 그 머신을 열 때" 자동으로 끝난다. |
어떻게 쓰나 · Claude Code 기준 최소 루프
# 첫 머신 · 정본 만들기 brew install chezmoi chezmoi init # ~/.local/share/chezmoi 가 source repo가 됨 chezmoi add ~/.claude/settings.json # 관리 대상 등록 chezmoi add ~/.claude/CLAUDE.md ~/.claude/rules/ # 머신별 분기 · 파일을 템플릿으로 승격 chezmoi chattr +template ~/.claude/settings.json # 템플릿 안에서: "impeccable@impeccable": {{ if eq .chezmoi.hostname "macbook-pro" }}true{{ else }}false{{ end }} # 시크릿 · 그 파일만 암호화 chezmoi add --encrypt ~/.claude/settings.local.json # 두 번째 머신부터 chezmoi init git@github.com:me/dotfiles.git chezmoi diff # 뭐가 바뀔지 먼저 확인 chezmoi apply
주의 두 가지, 둘 다 커뮤니티 실측에서 나온 것: ① 전체 동기화 금지: ~/.claude의 세션 로그·캐시·플러그인 캐시는 머신 고유물이라 add 대상이 아니다(선별 add가 기본). ② 마켓플레이스 플러그인 실체는 각 머신이 스스로 내려받는다. 동기화 대상은 settings.json의 선언(enabledPlugins)이지 캐시가 아니다. keystone의 "시드는 소스, 캐시는 머신별"과 같은 결론이다.
전환 비권장: 4프로파일 분기와 adapter export가 chezmoi 범위 밖이고, 이미 운영 중인 시스템을 옮기면 이득 없이 마이그레이션 비용만 남는다(§03 반론의 조건이 뒤집히기 전까지).
채굴 후보 ①: age 선별 암호화. 현재 settings.local.json은 "동기화 안 함"으로만 처리되는데, 암호화해 레이어에 실으면 새 머신 셋업에서 시크릿 재입력이 사라진다. ②: .chezmoiignore식 명시적 제외 선언. keystone의 머신 고유물 제외는 스크립트 로직 안에 흩어져 있는데, 선언 파일 하나로 모으면 감사가 쉬워진다. ③: autoPush식 세션 훅 pull. 지금은 "다음 apply 때 정합"인데, SessionStart에서 가벼운 pull 체크를 하면 머신 간 드리프트 창이 줄어든다.
05판정 · 반론 · 반증 조건
칭찬으로 끝나는 평가는 정보량이 없다. 이 글의 결론 세 개를 각각 반증 조건과 함께 적는다.
결론과 그것이 틀리는 조건
| 결론 | 반론 | 반증 조건 (이게 관측되면 결론 폐기) |
|---|---|---|
| keystone 방식은 커뮤니티 2층의 정석 패턴이다: git 소스 → 머신별 apply는 dotfiles 관행과 동형 | 동형이라는 사실이 비용까지 정당화하지는 않는다. chezmoi는 남이 유지보수하는 도구고 apply.sh는 우리가 유지보수하는 코드다. 사고 이력(런타임 키 증발, diverged 처리)은 전부 후자에서 났다 | 분기당 apply發 설정 사고가 반복 관측되면(예: 2회+) 설정 배포 부분만 chezmoi로 이관하는 RFC를 연다 |
| impeccable은 "전역 off + 프로젝트 on"이 맞다: 라우팅 충돌과 품질을 둘 다 잡는 구성 | 디자인 작업이 잦으면 프로젝트마다 켜는 마찰이 누적된다. 그 경우 keystone 레이어로 포크해 disable-model-invocation: true를 달고 전역 명시-호출 전용으로 두는 쪽이 낫다. 단 업스트림 churn(2.5개월에 메이저 1회) 추적 비용을 산다 |
한 달 안에 프로젝트 enable 커밋이 3개 repo를 넘으면 포크안으로 전환 검토 |
| chezmoi 전환은 비권장, 채굴만 한다: 4프로파일 + adapter export가 범위 밖 | "이미 만들어서 아깝다"가 판단을 오염시킬 수 있다(매몰 비용). 채굴 3건(age 암호화·명시적 제외 선언·세션 훅 pull)을 실제로 안 하면 이 판정은 현상 유지의 변명이었던 것 | 채굴 후보가 다음 분기까지 이슈로도 등록되지 않으면, 판정 자체를 재심사한다 |
스킬 활성/비활성은 이미 공식 레버가 다 있다(frontmatter · enabledPlugins 스코프 · 아카이브). 진짜 문제는 "어느 머신에서"이고, 그 답의 커뮤니티 표준은 user 스코프의 git 정본화다. keystone은 그걸 이미 하고 있으며, 오늘 impeccable 건은 그 시스템이 정상 작동하는 한 바퀴(소스 3줄 → PR #454 → apply → 실측)의 기록이다.
claude plugin --help·enable/disable scope 플래그 / 공식 skills 문서(disable-model-invocation·user-invocable) / keystone-hub layers 3종 settings.json·apply.sh runtime-preserve 소스 / PR #454 병합·apply 후 런타임 값·플러그인 4.0.4 실측 / 커뮤니티 소스 8건(§02 링크)