superpowers SKILL.md Not Found, depth 1 클론으로 스킬 15개 읽기
스킬을 고치거나 배포하려면 저장소 설명만으로는 모자라고, 디렉터리마다 들어 있는 본문과 참조 파일이 손에 있어야 합니다. 그래서 obra/superpowers를 업무용 스킬 구성에 들일지는 Debian 13에서 저장소 API와 클론만으로 실제 스킬 파일을 읽고 구조까지 검사할 수 있느냐로 판단했습니다. 외부 저장소를 가져오는 단계와 가져온 파일을 검증하는 단계를 떼어 두면 어디서 실패했는지가 훨씬 빨리 보입니다. 저장소 메타데이터를 스킬 목록으로 잘못 읽었을 때 나는 오류부터 재현하고, 필요한 파일 범위에 따라 조회 방법을 골랐습니다.
실행일은 2026-10-14입니다. 재현 스크립트로 저장소 JSON의 skills 키와 skills/SKILL.md 요청을 다시 일으켰고, 해결 스크립트로 depth 1 클론을 받아 SKILL.md 15개의 앞머리와 보조 파일을 집계했습니다. 점검 스크립트는 API의 주소·브랜치를 파일 목록과 맞춰 보고 diagnosing-superpowers의 구조 검사를 돌립니다. 명령 블록은 스크립트에서 떼어 온 부분이고, 출력 블록은 초기 설정과 중간 처리까지 포함한 전체 실행 결과입니다.
별 수, 포크 수, 열린 이슈 수, size, created_at, pushed_at, updated_at, 커밋 시각은 다시 돌리면 바뀔 수 있는 값입니다. 그래서 본문에는 그 숫자를 옮겨 적지 않고, 재현 스크립트와 해결 스크립트 출력의 필드 이름으로 가리킵니다. 이번 응답의 별 수도 출력의 stargazers_count에서 보시면 됩니다.
명령을 실행한 환경은 다음 표와 같습니다. 값은 실행 로그의 출력에서 옮겼고, 다른 실행 로그에도 같은 실행일이 찍혀 있습니다. nvidia-smi-absent는 해당 명령을 찾지 못했을 때 남긴 문자열입니다. 이 글의 판단 근거는 파일 조회와 구조 검사 결과이므로, 환경 정보는 그 결과를 다시 만들어 보기 위한 기준으로 읽으시면 됩니다.
| 항목 | 값 | 출력 |
|---|---|---|
| OS | Debian GNU/Linux 13 (trixie) | PRETTY_NAME |
| 커널 | Linux 6.12.94+ x86_64 | uname -srm |
| CPU | nproc 8 | nproc |
| git | git version 2.47.3 | git --version |
| Python | Python 3.13.5 | python3 --version |
| curl | curl 8.14.1 | curl --version 첫 줄 |
| 실행일 | 2026-10-14 | date -u +%Y-%m-%d와 TZ=Asia/Seoul date +%F |
| nvidia-smi | nvidia-smi-absent | command -v가 비었을 때의 echo |
저장소 API는 200인데 KeyError, skills/SKILL.md는 Not Found
obra/superpowers를 저장소 API로 조회하고, JSON 안에 스킬 목록이 있다고 가정했을 때 어디서 깨지는지 재현했습니다. 코드는 실행 환경을 기록하고 저장소 응답을 받은 뒤 d["skills"]를 읽고, 이와 별도로 skills/SKILL.md를 요청합니다. 저장소 응답은 HTTP 200이었는데도 KeyError: 'skills'가 났고, 파일 요청은 404였습니다. 저장소에 닿았다는 것과 파일 경로가 맞다는 것은 서로 다른 문제라는 점이 이 차이에서 드러납니다.
grep ^PRETTY_NAME= /etc/os-release
uname -srm
nproc
git --version
python3 --version
curl --version | head -n 1
date -u +%Y-%m-%d
TZ=Asia/Seoul date +%F
command -v nvidia-smi || echo nvidia-smi-absent
curl -sS --max-time 20 -A "$UA" -o tmp/repo.json -w 'repo_http=%{http_code}\n' https://api.github.com/repos/obra/superpowers
try:
d["skills"]
except KeyError:
print(traceback.format_exc(), end="")
curl -sS --max-time 20 -A "$UA" -o tmp/miss.json -w 'miss_http=%{http_code}\n' https://api.github.com/repos/obra/superpowers/contents/skills/SKILL.md
저장소 접근과 파일 접근이 서로 다른 결과를 낸 기록입니다. repo_http=200인데 has_skills_key는 False이고, 키를 직접 읽은 줄에서 KeyError: 'skills'가 납니다. 뒤따르는 miss_http=404와 Not Found는 요청한 파일 경로에 대한 답입니다. 재시도 횟수를 늘릴 일이 아니라, JSON에서 무엇을 읽는지와 어떤 경로를 부르는지부터 고쳐야 한다는 뜻입니다.
+ grep ^PRETTY_NAME= /etc/os-release
PRETTY_NAME="Debian GNU/Linux 13 (trixie)"
+ uname -srm
Linux 6.12.94+ x86_64
+ nproc
8
+ git --version
git version 2.47.3
+ python3 --version
Python 3.13.5
+ curl --version | head -n 1
curl 8.14.1 (x86_64-pc-linux-gnu) libcurl/8.14.1 OpenSSL/3.5.7 zlib/1.3.1 brotli/1.1.0 zstd/1.5.7 libidn2/2.3.8 libpsl/0.21.2 libssh2/1.11.1 nghttp2/1.64.0 nghttp3/1.8.0 librtmp/2.3 OpenLDAP/2.6.10
+ date -u +%Y-%m-%d
2026-10-14
+ TZ=Asia/Seoul date +%F
2026-10-14
+ command -v nvidia-smi || echo nvidia-smi-absent
nvidia-smi-absent
+ curl -sS --max-time 20 -A ainewsnote-guide/2026-10-14 -o tmp/repo.json -w repo_http=%{http_code} https://api.github.com/repos/obra/superpowers
repo_http=200
+ python3 -u tmp/repo.json fields and d["skills"]
full_name obra/superpowers
description An agentic skills framework & software development methodology that works.
default_branch main
license_spdx MIT
language Shell
stargazers_count 298786
forks_count 26711
open_issues_count 341
size 7658
created_at 2025-10-09T19:45:18Z
pushed_at 2026-10-10T03:03:30Z
updated_at 2026-10-14T06:28:46Z
html_url https://github.com/obra/superpowers
has_skills_key False
key_count 84
Traceback (most recent call last):
File "<stdin>", line 20, in <module>
KeyError: 'skills'
+ curl -sS --max-time 20 -A ainewsnote-guide/2026-10-14 -o tmp/miss.json -w miss_http=%{http_code} https://api.github.com/repos/obra/superpowers/contents/skills/SKILL.md
miss_http=404
+ python3 tmp/miss.json message
api_message Not Found
status 404
documentation_url https://docs.github.com/rest/repos/contents#get-repository-content
reproduce_done=1
터미널 캡처에서도 HTTP 200 응답 바로 뒤에 KeyError: 'skills'가 찍힙니다. 통신이 성공했다고 스킬 조회까지 끝난 것은 아니라는 이야기입니다. skills/SKILL.md의 404는 저장소 안의 디렉터리 구조를 따라 경로를 다시 잡으라는 신호로 읽었습니다.

필드를 출력한 뒤 실패한 지점은 저장소 JSON의 skills 키를 읽는 부분입니다. 응답에 있던 full_name, default_branch, license_spdx는 저장소 식별에 쓰고, 스킬 파일은 별도 경로로 찾아야 합니다. 오류 응답의 documentation_url과 영문 contents 문서, 저장소 리소스 문서는 출력에서 link_status 200으로 남았습니다. 링크 접근 결과와 실제로 받은 JSON 필드를 함께 두면, 문서 주소의 응답과 요청 대상 파일의 404를 혼동하지 않을 수 있습니다.
저장소 메타데이터와 스킬 경로를 헷갈린 것이 원인
저장소 리소스의 JSON은 이름, 설명, 라이선스, 기본 브랜치, 시각, 별 수 같은 저장소 필드를 담습니다. 이번 키 84개 안에 skills는 없습니다. 그래서 그 키를 파일 목록으로 가정한 읽기는 KeyError: 'skills'를 발생시킵니다. 스킬 본문은 다른 경로에 있습니다. skills/SKILL.md라는 파일 하나로는 존재하지 않고, 각 스킬은 skills/ 아래의 디렉터리이며 그 안의 SKILL.md가 본문입니다. 없는 파일 경로를 contents API에 넘기면 message가 Not Found이고 status가 404입니다.
저장소 API는 한 번의 호출로 full_name, default_branch를 주는 데는 충분합니다. 그러나 skills 키가 없어서 스킬 파일은 이 응답에서 나오지 않고, skills/SKILL.md를 부르면 Not Found가 돌아옵니다.
depth 1 클론으로 SKILL.md 15개를 손에 넣기
해결 스크립트는 작업 디렉터리의 sp를 비운 뒤 https://github.com/obra/superpowers를 --depth 1로 받습니다. 인증 프롬프트에 멈추지 않도록 GIT_TERMINAL_PROMPT=0을 두고, 색 출력을 끈 뒤 timeout을 걸어 받습니다. 이번 clone_exit는 0입니다. git -C sp rev-parse --abbrev-ref HEAD는 main이고, 이는 API의 default_branch와 같습니다. 커밋 해시는 출력의 rev-parse HEAD 줄에 있습니다. clone 명령의 문서 주소인 git-clone은 link_status 200이었습니다.
rm -rf sp
timeout 45 git clone --depth 1 https://github.com/obra/superpowers sp
git -C sp rev-parse --abbrev-ref HEAD
git -C sp rev-parse HEAD
git -C sp log -1 --format='%h %ci'
ls --color=never sp
ls --color=never sp/skills
find sp/skills -name SKILL.md | wc -l
find sp/skills -mindepth 1 -maxdepth 1 -type d | wc -l
클론 종료 값과 받은 트리의 내용을 한꺼번에 봅니다. clone_exit=0은 내려받기가 성공했다는 뜻이고, 디렉터리 수와 SKILL.md 수는 둘 다 15입니다. 브랜치는 main이고, 커밋 해시는 API의 브랜치 응답과 맞춰 볼 기준이 됩니다. --depth 1이 이번 수집 범위였으니, 이어지는 결과도 그때 받은 트리를 기준으로 읽어야 합니다.
+ date -u +%Y-%m-%d
2026-10-14
+ rm -rf sp
+ timeout 45 git clone --depth 1 https://github.com/obra/superpowers sp
Cloning into 'sp'...
remote: Enumerating objects: 309, done.
remote: Counting objects: 0% (1/309) remote: Counting objects: 1% (4/309) remote: Counting objects: 2% (7/309) remote: Counting objects: 3% (10/309) remote: Counting objects: 4% (13/309) remote: Counting objects: 5% (16/309) remote: Counting objects: 6% (19/309) remote: Counting objects: 7% (22/309) remote: Counting objects: 8% (25/309) remote: Counting objects: 9% (28/309) remote: Counting objects: 10% (31/309) remote: Counting objects: 11% (34/309) remote: Counting objects: 12% (38/309) remote: Counting objects: 13% (41/309) remote: Counting objects: 14% (44/309) remote: Counting objects: 15% (47/309) remote: Counting objects: 16% (50/309) remote: Counting objects: 17% (53/309) remote: Counting objects: 18% (56/309) remote: Counting objects: 19% (59/309) remote: Counting objects: 20% (62/309) remote: Counting objects: 21% (65/309) remote: Counting objects: 22% (68/309) remote: Counting objects: 23% (72/309) remote: Counting objects: 24% (75/309) remote: Counting objects: 25% (78/309) remote: Counting objects: 26% (81/309) remote: Counting objects: 27% (84/309) remote: Counting objects: 28% (87/309) remote: Counting objects: 29% (90/309) remote: Counting objects: 30% (93/309) remote: Counting objects: 31% (96/309) remote: Counting objects: 32% (99/309) remote: Counting objects: 33% (102/309) remote: Counting objects: 34% (106/309) remote: Counting objects: 35% (109/309) remote: Counting objects: 36% (112/309) remote: Counting objects: 37% (115/309) remote: Counting objects: 38% (118/309) remote: Counting objects: 39% (121/309) remote: Counting objects: 40% (124/309) remote: Counting objects: 41% (127/309) remote: Counting objects: 42% (130/309) remote: Counting objects: 43% (133/309) remote: Counting objects: 44% (136/309) remote: Counting objects: 45% (140/309) remote: Counting objects: 46% (143/309) remote: Counting objects: 47% (146/309) remote: Counting objects: 48% (149/309) remote: Counting objects: 49% (152/309) remote: Counting objects: 50% (155/309) remote: Counting objects: 51% (158/309) remote: Counting objects: 52% (161/309) remote: Counting objects: 53% (164/309) remote: Counting objects: 54% (167/309) remote: Counting objects: 55% (170/309) remote: Counting objects: 56% (174/309) remote: Counting objects: 57% (177/309) remote: Counting objects: 58% (180/309) remote: Counting objects: 59% (183/309) remote: Counting objects: 60% (186/309) remote: Counting objects: 61% (189/309) remote: Counting objects: 62% (192/309) remote: Counting objects: 63% (195/309) remote: Counting objects: 64% (198/309) remote: Counting objects: 65% (201/309) remote: Counting objects: 66% (204/309) remote: Counting objects: 67% (208/309) remote: Counting objects: 68% (211/309) remote: Counting objects: 69% (214/309) remote: Counting objects: 70% (217/309) remote: Counting objects: 71% (220/309) remote: Counting objects: 72% (223/309) remote: Counting objects: 73% (226/309) remote: Counting objects: 74% (229/309) remote: Counting objects: 75% (232/309) remote: Counting objects: 76% (235/309) remote: Counting objects: 77% (238/309) remote: Counting objects: 78% (242/309) remote: Counting objects: 79% (245/309) remote: Counting objects: 80% (248/309) remote: Counting objects: 81% (251/309) remote: Counting objects: 82% (254/309) remote: Counting objects: 83% (257/309) remote: Counting objects: 84% (260/309) remote: Counting objects: 85% (263/309) remote: Counting objects: 86% (266/309) remote: Counting objects: 87% (269/309) remote: Counting objects: 88% (272/309) remote: Counting objects: 89% (276/309) remote: Counting objects: 90% (279/309) remote: Counting objects: 91% (282/309) remote: Counting objects: 92% (285/309) remote: Counting objects: 93% (288/309) remote: Counting objects: 94% (291/309) remote: Counting objects: 95% (294/309) remote: Counting objects: 96% (297/309) remote: Counting objects: 97% (300/309) remote: Counting objects: 98% (303/309) remote: Counting objects: 99% (306/309) remote: Counting objects: 100% (309/309) remote: Counting objects: 100% (309/309), done.
remote: Compressing objects: 0% (1/275) remote: Compressing objects: 1% (3/275) remote: Compressing objects: 2% (6/275) remote: Compressing objects: 3% (9/275) remote: Compressing objects: 4% (11/275) remote: Compressing objects: 5% (14/275) remote: Compressing objects: 6% (17/275) remote: Compressing objects: 7% (20/275) remote: Compressing objects: 8% (22/275) remote: Compressing objects: 9% (25/275) remote: Compressing objects: 10% (28/275) remote: Compressing objects: 11% (31/275) remote: Compressing objects: 12% (33/275) remote: Compressing objects: 13% (36/275) remote: Compressing objects: 14% (39/275) remote: Compressing objects: 15% (42/275) remote: Compressing objects: 16% (44/275) remote: Compressing objects: 17% (47/275) remote: Compressing objects: 18% (50/275) remote: Compressing objects: 19% (53/275) remote: Compressing objects: 20% (55/275) remote: Compressing objects: 21% (58/275) remote: Compressing objects: 22% (61/275) remote: Compressing objects: 23% (64/275) remote: Compressing objects: 24% (66/275) remote: Compressing objects: 25% (69/275) remote: Compressing objects: 26% (72/275) remote: Compressing objects: 27% (75/275) remote: Compressing objects: 28% (77/275) remote: Compressing objects: 29% (80/275) remote: Compressing objects: 30% (83/275) remote: Compressing objects: 31% (86/275) remote: Compressing objects: 32% (88/275) remote: Compressing objects: 33% (91/275) remote: Compressing objects: 34% (94/275) remote: Compressing objects: 35% (97/275) remote: Compressing objects: 36% (99/275) remote: Compressing objects: 37% (102/275) remote: Compressing objects: 38% (105/275) remote: Compressing objects: 39% (108/275) remote: Compressing objects: 40% (110/275) remote: Compressing objects: 41% (113/275) remote: Compressing objects: 42% (116/275) remote: Compressing objects: 43% (119/275) remote: Compressing objects: 44% (121/275) remote: Compressing objects: 45% (124/275) remote: Compressing objects: 46% (127/275) remote: Compressing objects: 47% (130/275) remote: Compressing objects: 48% (132/275) remote: Compressing objects: 49% (135/275) remote: Compressing objects: 50% (138/275) remote: Compressing objects: 51% (141/275) remote: Compressing objects: 52% (143/275) remote: Compressing objects: 53% (146/275) remote: Compressing objects: 54% (149/275) remote: Compressing objects: 55% (152/275) remote: Compressing objects: 56% (154/275) remote: Compressing objects: 57% (157/275) remote: Compressing objects: 58% (160/275) remote: Compressing objects: 59% (163/275) remote: Compressing objects: 60% (165/275) remote: Compressing objects: 61% (168/275) remote: Compressing objects: 62% (171/275) remote: Compressing objects: 63% (174/275) remote: Compressing objects: 64% (176/275) remote: Compressing objects: 65% (179/275) remote: Compressing objects: 66% (182/275) remote: Compressing objects: 67% (185/275) remote: Compressing objects: 68% (187/275) remote: Compressing objects: 69% (190/275) remote: Compressing objects: 70% (193/275) remote: Compressing objects: 71% (196/275) remote: Compressing objects: 72% (198/275) remote: Compressing objects: 73% (201/275) remote: Compressing objects: 74% (204/275) remote: Compressing objects: 75% (207/275) remote: Compressing objects: 76% (209/275) remote: Compressing objects: 77% (212/275) remote: Compressing objects: 78% (215/275) remote: Compressing objects: 79% (218/275) remote: Compressing objects: 80% (220/275) remote: Compressing objects: 81% (223/275) remote: Compressing objects: 82% (226/275) remote: Compressing objects: 83% (229/275) remote: Compressing objects: 84% (231/275) remote: Compressing objects: 85% (234/275) remote: Compressing objects: 86% (237/275) remote: Compressing objects: 87% (240/275) remote: Compressing objects: 88% (242/275) remote: Compressing objects: 89% (245/275) remote: Compressing objects: 90% (248/275) remote: Compressing objects: 91% (251/275) remote: Compressing objects: 92% (253/275) remote: Compressing objects: 93% (256/275) remote: Compressing objects: 94% (259/275) remote: Compressing objects: 95% (262/275) remote: Compressing objects: 96% (264/275) remote: Compressing objects: 97% (267/275) remote: Compressing objects: 98% (270/275) remote: Compressing objects: 99% (273/275) remote: Compressing objects: 100% (275/275) remote: Compressing objects: 100% (275/275), done.
remote: Total 309 (delta 9), reused 239 (delta 8), pack-reused 0 (from 0)
Receiving objects: 100% (309/309), 793.88 KiB | 1.11 MiB/s, done.
Resolving deltas: 100% (9/9), done.
clone_exit=0
+ git -C sp rev-parse --abbrev-ref HEAD
main
+ git -C sp rev-parse HEAD
bb92a77741419a4ab5f06e711a283343f1ada0c3
+ git -C sp log -1 --format=%h %ci
bb92a77 2026-10-09 17:28:04 -0700
+ ls --color=never sp
AGENTS.md GEMINI.md README.md assets gemini-extension.json index.js scripts tests
CODE_OF_CONDUCT.md LICENSE RELEASE-NOTES.md docs hooks package.json skills
+ ls --color=never sp/skills
brainstorming finishing-a-development-branch systematic-debugging verification-before-completion
diagnosing-superpowers receiving-code-review test-driven-development writing-plans
dispatching-parallel-agents requesting-code-review using-git-worktrees writing-skills
executing-plans subagent-driven-development using-superpowers
+ find sp/skills -name SKILL.md | wc -l
15
+ find sp/skills -mindepth 1 -maxdepth 1 -type d | wc -l
15
+ python3 inventory of skills/*/SKILL.md
skill name_match use_when desc_len body_words extra_files h1
brainstorming yes no 213 1686 7 Brainstorming
tops brainstorming builder-check-prompt.md,scripts,visual-companion.md
desc brainstorming You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or planning anything new, in software or out of it (a talk, a business, a renovation).
first_body brainstorming # Brainstorming
diagnosing-superpowers yes yes 375 991 19 Diagnosing Superpowers
tops diagnosing-superpowers prompts,references,templates
dispatching-parallel-agents yes yes 106 843 0 Dispatching Parallel Agents
tops dispatching-parallel-agents -
executing-plans yes yes 170 3235 2 Executing Plans
tops executing-plans scripts
finishing-a-development-branch yes yes 101 1246 0 Finishing a Development Branch
tops finishing-a-development-branch -
receiving-code-review yes yes 234 879 0 Code Review Reception
tops receiving-code-review -
requesting-code-review yes yes 107 405 1 Requesting Code Review
tops requesting-code-review code-reviewer.md
subagent-driven-development yes yes 85 4854 6 Subagent-Driven Development
tops subagent-driven-development implementer-prompt.md,re-review-prompt.md,scripts,task-reviewer-prompt.md
systematic-debugging yes yes 91 1436 10 Systematic Debugging
tops systematic-debugging CREATION-LOG.md,condition-based-waiting-example.ts,condition-based-waiting.md,defense-in-depth.md,find-polluter.sh,root-cause-tracing.md,test-academic.md,test-pressure-1.md,test-pressure-2.md,test-pressure-3.md
test-driven-development yes yes 79 1459 1 Test-Driven Development (TDD)
tops test-driven-development writing-good-tests.md
using-git-worktrees yes yes 196 1035 0 Using Git Worktrees
tops using-git-worktrees -
using-superpowers yes yes 154 465 7 -
tops using-superpowers references
desc using-superpowers Use when starting any conversation - establishes how to find and use skills, requiring skill invocation before ANY response including clarifying questions
first_body using-superpowers <SUBAGENT-STOP>
verification-before-completion yes yes 225 542 0 Verification Before Completion
tops verification-before-completion -
writing-plans yes yes 84 1619 0 Writing Plans
tops writing-plans -
writing-skills yes yes 97 3795 6 Writing Skills
tops writing-skills anthropic-best-practices.md,examples,graphviz-conventions.dot,persuasion-principles.md,render-graphs.js,testing-skills-with-subagents.md
skill_dir_count 15
skill_md_count 15
name_match_count 15
use_when_count 14
use_when_missing brainstorming
extra_zero_count 6
extra_zero dispatching-parallel-agents finishing-a-development-branch receiving-code-review using-git-worktrees verification-before-completion writing-plans
readme_listed_count 15
readme_missing_dirs -
readme_extra_names -
desc_len_min 79 test-driven-development
desc_len_max 375 diagnosing-superpowers
body_words_min 405 requesting-code-review
body_words_max 4854 subagent-driven-development
+ python3 sp/.claude-plugin/plugin.json
plugin_name superpowers
plugin_version 7.0.0
plugin_license MIT
plugin_description Core skills library for Claude Code: TDD, debugging, collaboration patterns, and proven techniques
plugin_author Jesse Vincent
plugin_homepage https://github.com/obra/superpowers
plugin_repository https://github.com/obra/superpowers
+ grep -n -F Max 1024 characters total sp/skills/writing-skills/SKILL.md
97:- Max 1024 characters total
+ grep -n -F Keep under 500 characters if possible sp/skills/writing-skills/SKILL.md
103: - Keep under 500 characters if possible
+ grep -n -F Start with "Use when..." sp/skills/writing-skills/SKILL.md
100: - Start with "Use when..." to focus on triggering conditions
148:**Format:** Start with "Use when..." to focus on triggering conditions
+ grep -n -F Flat namespace sp/skills/writing-skills/SKILL.md
82:**Flat namespace** - all skills in one searchable namespace
+ grep -n -F SKILL.md # Main reference (required) sp/skills/writing-skills/SKILL.md
78: SKILL.md # Main reference (required)
+ grep -n -F Installation differs by harness. sp/README.md
54:Installation differs by harness. If you use more than one, install Superpowers separately for each one.
+ grep -n -F skills trigger automatically sp/README.md
46:There's a bunch more to it, but that's the core of the system. And because the skills trigger automatically, you don't need to do anything special. Your coding agent just has Superpowers.
SOLVED clone_exit=0
SOLVED skill_md_count=15
해결 쪽 캡처는 트리를 내려받고 스킬 정보를 집계한 화면입니다. clone_exit=0과 skill_md_count=15가 한 화면에 있으니, 파일을 받은 단계와 본문을 찾은 단계가 각각 성공했는지 따로 확인됩니다. 이 트리에는 스킬 본문 말고도 보조 파일이 들어 있어서, 배포할 때 본문만 복사해 가면 참조가 끊길 수 있습니다. 참조 관계까지 함께 옮겨야 합니다.

클론 루트의 ls는 AGENTS.md, CODE_OF_CONDUCT.md, GEMINI.md, LICENSE, README.md, RELEASE-NOTES.md, assets, docs, gemini-extension.json, hooks, index.js, package.json, scripts, skills, tests를 출력했습니다. skills 안의 디렉터리 수와 SKILL.md 수는 둘 다 15입니다. 마지막 두 줄은 SOLVED clone_exit=0과 SOLVED skill_md_count=15입니다.
다음 표는 각 SKILL.md 앞머리의 name·description과 본문·보조 파일 집계를 묶은 것입니다. 본문 단어 수는 앞머리 뒤를 공백으로 나눈 개수, 보조 파일 수는 해당 디렉터리에서 SKILL.md를 뺀 파일 수이고, H1은 처음 나오는 제목이 없으면 -로 적었습니다. 앞머리 이름과 디렉터리 이름은 15개 모두 일치합니다. 그런데 Use when으로 시작하는 description은 14개뿐이라, 이름이 맞는다고 문장 형식까지 맞는 것은 아닙니다. 본문 단어 수는 405에서 4854까지 벌어지고 보조 파일이 없는 스킬은 6개이니, 스킬마다 본문 분량도 옮길 파일 범위도 제각각입니다.
| 디렉터리 | Use when | description 글자 | 본문 단어 | 보조 파일 | H1 |
|---|---|---|---|---|---|
| brainstorming | no | 213 | 1686 | 7 | Brainstorming |
| diagnosing-superpowers | yes | 375 | 991 | 19 | Diagnosing Superpowers |
| dispatching-parallel-agents | yes | 106 | 843 | 0 | Dispatching Parallel Agents |
| executing-plans | yes | 170 | 3235 | 2 | Executing Plans |
| finishing-a-development-branch | yes | 101 | 1246 | 0 | Finishing a Development Branch |
| receiving-code-review | yes | 234 | 879 | 0 | Code Review Reception |
| requesting-code-review | yes | 107 | 405 | 1 | Requesting Code Review |
| subagent-driven-development | yes | 85 | 4854 | 6 | Subagent-Driven Development |
| systematic-debugging | yes | 91 | 1436 | 10 | Systematic Debugging |
| test-driven-development | yes | 79 | 1459 | 1 | Test-Driven Development (TDD) |
| using-git-worktrees | yes | 196 | 1035 | 0 | Using Git Worktrees |
| using-superpowers | yes | 154 | 465 | 7 | – |
| verification-before-completion | yes | 225 | 542 | 0 | Verification Before Completion |
| writing-plans | yes | 84 | 1619 | 0 | Writing Plans |
| writing-skills | yes | 97 | 3795 | 6 | Writing Skills |
H1은 디렉터리 이름과 항상 같지 않습니다. receiving-code-review의 H1은 Code Review Reception이고, test-driven-development의 H1은 Test-Driven Development (TDD)입니다. using-superpowers의 H1 칸은 -입니다. 그 파일의 첫 본문 줄은 <SUBAGENT-STOP>이고, description은 “Use when starting any conversation – establishes how to find and use skills, requiring skill invocation before ANY response including clarifying questions”입니다. brainstorming의 description은 “You MUST use this before any creative work – creating features, building components, adding functionality, modifying behavior, or planning anything new, in software or out of it (a talk, a business, a renovation).”이고, 첫 본문 줄은 # Brainstorming입니다.
extra_zero에 없는 디렉터리의 최상위 이름은 출력의 tops 줄과 같습니다. brainstorming은 builder-check-prompt.md, scripts, visual-companion.md입니다. diagnosing-superpowers는 prompts, references, templates입니다. executing-plans는 scripts입니다. requesting-code-review는 code-reviewer.md입니다. subagent-driven-development는 implementer-prompt.md, re-review-prompt.md, scripts, task-reviewer-prompt.md입니다. systematic-debugging은 CREATION-LOG.md, condition-based-waiting-example.ts, condition-based-waiting.md, defense-in-depth.md, find-polluter.sh, root-cause-tracing.md, test-academic.md, test-pressure-1.md, test-pressure-2.md, test-pressure-3.md입니다. test-driven-development는 writing-good-tests.md입니다. using-superpowers는 references입니다. writing-skills는 anthropic-best-practices.md, examples, graphviz-conventions.dot, persuasion-principles.md, render-graphs.js, testing-skills-with-subagents.md입니다.
README에서 ### Skills Library와 ## Philosophy 사이를 잘라 소문자 굵은 이름을 세면 readme_listed_count는 15입니다. readme_missing_dirs와 readme_extra_names는 둘 다 -입니다. 디렉터리 15개와 README에 적힌 이름 15개가 서로 빠지거나 남지 않았습니다. README의 링크 상태도 200이었습니다.
writing-skills의 SKILL.md는 같은 클론에서 읽었습니다. 링크 상태는 200입니다. grep이 찾은 줄은 앞머리 전체에 대해 “Max 1024 characters total”, description에 대해 “Keep under 500 characters if possible”, “Start with “Use when…” to focus on triggering conditions”입니다. Use when으로 시작하는 그 문장은 grep 출력의 100행과 148행에 있습니다. 같은 파일은 “Flat namespace”와 “SKILL.md # Main reference (required)”를 적고 있습니다. 이번 desc_len은 79에서 375 사이라, 그 파일이 적은 500자 선호 구간 안에 있습니다. 1024는 그 파일이 앞머리 전체에 적어 둔 문장이고, 이번 집계가 잰 값은 description 길이입니다.
.claude-plugin/plugin.json의 plugin_name은 superpowers이고, plugin_version은 7.0.0이며, plugin_license는 MIT입니다. 라이선스 문자열은 API의 license_spdx와 같습니다. plugin_author는 Jesse Vincent입니다. plugin_homepage와 plugin_repository는 둘 다 저장소 URL입니다. plugin_description은 “Core skills library for Claude Code: TDD, debugging, collaboration patterns, and proven techniques”입니다. API description과 문장이 다릅니다. 어느 쪽을 인용하는지는 필드 이름을 남기는 편이 안전합니다. README 54행은 Installation differs by harness. If you use more than one, install Superpowers separately for each one.입니다. 46행에는 “the skills trigger automatically”가 있습니다. 이번 스크립트의 실행 범위는 클론된 트리 읽기입니다.
클론 방식의 이점은 SKILL.md 본문, 보조 파일, plugin.json의 버전을 한 트리에서 모두 읽는다는 데 있습니다. 대신 저장소를 통째로 받아야 하고, 별 수나 갱신 시각처럼 저장소 자체의 정보는 저장소 API 필드 쪽이 더 직접적입니다.
clone_url, contents API, 구조 검사로 범위 좁히기
저장소 API에서 clone_url을 읽고, default_branch인 main을 branches API로 다시 조회합니다. 이어서 contents API로 디렉터리 이름만 받고, raw URL로 writing-skills의 SKILL.md 앞머리를 읽습니다. 구조 검사는 해결 스크립트가 만들어 둔 sp 트리에서 diagnosing-superpowers 스크립트만 실행합니다. 해결 스크립트의 depth 1 클론은 clone_exit=0이었습니다.
curl -sS --max-time 20 -A "$UA" -o tmp/repo.json -w 'repo_http=%{http_code}\n' https://api.github.com/repos/obra/superpowers
curl -sS --max-time 20 -A "$UA" -o tmp/branch.json -w 'branch_http=%{http_code}\n' https://api.github.com/repos/obra/superpowers/branches/main
curl -sS --max-time 20 -A "$UA" -o tmp/skills.json -w 'contents_http=%{http_code}\n' https://api.github.com/repos/obra/superpowers/contents/skills
curl -sS --max-time 20 -A "$UA" -o tmp/writing-skills.md -w 'raw_http=%{http_code}\n' https://raw.githubusercontent.com/obra/superpowers/main/skills/writing-skills/SKILL.md
bash sp/tests/diagnosing-superpowers/test-skill-structure.sh
주소 확인, 목록 조회, 파일 읽기, 구조 검사가 순서대로 이어집니다. contents_dir_count=15와 raw_http=200으로 디렉터리 목록과 지정 파일을 각각 받았고, 구조 검사는 Passed: 46 Failed: 0과 structure_test_exit=0으로 끝났습니다. 이 통과는 어디까지나 diagnosing-superpowers에 걸린 검사의 결과로 기록해 둡니다.
+ date -u +%Y-%m-%d
2026-10-14
+ curl -sS --max-time 20 -A ainewsnote-guide/2026-10-14 -o tmp/repo.json -w repo_http=%{http_code} https://api.github.com/repos/obra/superpowers
repo_http=200
+ python3 tmp/repo.json clone_url
full_name obra/superpowers
clone_url https://github.com/obra/superpowers.git
default_branch main
clone_url_matches_full_name yes
SOLVED clone_url_matches_full_name=yes
+ curl -sS --max-time 20 -A ainewsnote-guide/2026-10-14 -o tmp/branch.json -w branch_http=%{http_code} https://api.github.com/repos/obra/superpowers/branches/main
branch_http=200
+ python3 tmp/branch.json name
branch_name main
branch_protected True
branch_sha bb92a77741419a4ab5f06e711a283343f1ada0c3
SOLVED branch_name=main
+ curl -sS --max-time 20 -A ainewsnote-guide/2026-10-14 -o tmp/skills.json -w contents_http=%{http_code} https://api.github.com/repos/obra/superpowers/contents/skills
contents_http=200
+ python3 tmp/skills.json names
contents_count 15
contents_dir_count 15
contents_other_count 0
contents_dir brainstorming
contents_dir diagnosing-superpowers
contents_dir dispatching-parallel-agents
contents_dir executing-plans
contents_dir finishing-a-development-branch
contents_dir receiving-code-review
contents_dir requesting-code-review
contents_dir subagent-driven-development
contents_dir systematic-debugging
contents_dir test-driven-development
contents_dir using-git-worktrees
contents_dir using-superpowers
contents_dir verification-before-completion
contents_dir writing-plans
contents_dir writing-skills
SOLVED contents_dir_count=15
+ curl -sS --max-time 20 -A ainewsnote-guide/2026-10-14 -o tmp/writing-skills.md -w raw_http=%{http_code} https://raw.githubusercontent.com/obra/superpowers/main/skills/writing-skills/SKILL.md
raw_http=200
+ python3 tmp/writing-skills.md frontmatter
raw_line ---
raw_line name: writing-skills
raw_line description: Use when creating new skills, editing existing skills, or verifying skills work before deployment
raw_line ---
+ curl -sI -L --max-time 20 -o /dev/null -w %{http_code} https://github.com/obra/superpowers
link_status 200 https://github.com/obra/superpowers
+ curl -sI -L --max-time 20 -o /dev/null -w %{http_code} https://github.com/obra/superpowers/blob/main/README.md
link_status 200 https://github.com/obra/superpowers/blob/main/README.md
+ curl -sI -L --max-time 20 -o /dev/null -w %{http_code} https://github.com/obra/superpowers/blob/main/skills/writing-skills/SKILL.md
link_status 200 https://github.com/obra/superpowers/blob/main/skills/writing-skills/SKILL.md
+ curl -sI -L --max-time 20 -o /dev/null -w %{http_code} https://docs.github.com/en/rest/repos/repos
link_status 200 https://docs.github.com/en/rest/repos/repos
+ curl -sI -L --max-time 20 -o /dev/null -w %{http_code} https://docs.github.com/en/rest/repos/contents
link_status 200 https://docs.github.com/en/rest/repos/contents
+ curl -sI -L --max-time 20 -o /dev/null -w %{http_code} https://docs.github.com/rest/repos/contents
link_status 200 https://docs.github.com/rest/repos/contents
+ curl -sI -L --max-time 20 -o /dev/null -w %{http_code} https://git-scm.com/docs/git-clone
link_status 200 https://git-scm.com/docs/git-clone
+ bash sp/tests/diagnosing-superpowers/test-skill-structure.sh
diagnosing-superpowers structure
[PASS] SKILL.md exists
[PASS] frontmatter name is diagnosing-superpowers
[PASS] description starts with 'Use when'
[PASS] description under 1024 characters
[PASS] description avoids workflow word 'dispatch'
[PASS] description avoids workflow word 'then'
[PASS] description avoids workflow word 'step'
[PASS] SKILL.md body within 1000 words (991)
[PASS] SKILL.md has section '## Hard rules'
[PASS] SKILL.md has section '## Red Flags'
[PASS] referenced file exists: prompts/analyst-common.md
[PASS] referenced file exists: prompts/scrub-audit.md
[PASS] referenced file exists: prompts/scrub.md
[PASS] referenced file exists: prompts/similar-session.md
[PASS] referenced file exists: references/context-safety.md
[PASS] referenced file exists: references/github-issues.md
[PASS] referenced file exists: references/session-discovery.md
[PASS] referenced file exists: templates/bundle-README.md
[PASS] referenced file exists: templates/case.md
[PASS] referenced file exists: templates/issue.md
[PASS] referenced file exists: templates/report.md
[PASS] expected file present: references/redaction-policy.md
[PASS] expected file present: references/session-discovery.md
[PASS] expected file present: references/context-safety.md
[PASS] expected file present: references/github-issues.md
[PASS] expected file present: prompts/analyst-common.md
[PASS] expected file present: prompts/skill-timeline.md
[PASS] expected file present: prompts/plan-adherence.md
[PASS] expected file present: prompts/repeated-work.md
[PASS] expected file present: prompts/stumbles.md
[PASS] expected file present: prompts/quality-evidence.md
[PASS] expected file present: prompts/request-conflicts.md
[PASS] expected file present: prompts/cost-and-time.md
[PASS] expected file present: prompts/scrub.md
[PASS] expected file present: prompts/scrub-audit.md
[PASS] expected file present: prompts/similar-session.md
[PASS] expected file present: templates/case.md
[PASS] expected file present: templates/report.md
[PASS] expected file present: templates/bundle-README.md
[PASS] expected file present: templates/issue.md
[PASS] removed reference absent: references/claude-code-sessions.md
[PASS] removed reference absent: references/codex-sessions.md
[PASS] removed reference absent: references/other-harnesses.md
[PASS] active skill prose has no references to removed harness recipes
[PASS] no machine-specific paths or names in shipped files (skills + tests)
[PASS] skill files say 'your human partner', not 'the user'
Passed: 46 Failed: 0
structure_test_exit=0
점검 캡처에서 Passed: 46 Failed: 0은 diagnosing-superpowers 검사에 실패 항목이 없었다는 뜻이고, 종료 값도 0입니다. 자동화에 넣는다면 이 결과를 파일 검증의 근거로 삼고, 외부 API의 응답 상태는 수집 단계의 상태로 따로 남기는 구성이 맞습니다.

이번 repo_http는 200입니다. full_name은 obra/superpowers이고, clone_url은 https://github.com/obra/superpowers.git이며, default_branch는 main입니다. clone_url_matches_full_name은 yes이고, 성공 줄은 SOLVED clone_url_matches_full_name=yes입니다. branch_http는 200이고, branch_name은 main이며, branch_protected는 True입니다. branch_sha 줄은 해결 스크립트 출력의 rev-parse HEAD와 같은 해시입니다. 성공 줄은 SOLVED branch_name=main입니다.
clone_url과 branches/main은 저장소를 받기 전에 주소와 기본 브랜치 이름을 미리 확인하게 해 줍니다. 다만 SKILL.md 본문은 여기서 나오지 않습니다.
클론 없이 디렉터리 이름만 필요하면 contents API의 contents/skills가 대안입니다. 이번 contents_http는 200이고, contents_count는 15이며, contents_dir_count는 15이고, contents_other_count는 0입니다. 항목은 모두 디렉터리입니다. contents_dir로 찍힌 이름은 클론의 ls sp/skills와 같은 15개입니다. 성공 줄은 SOLVED contents_dir_count=15입니다. raw 주소로 받은 writing-skills의 SKILL.md는 raw_http 200이고, 앞 네 줄은 앞머리 구분자, name: writing-skills, Use when으로 시작하는 description, 다시 앞머리 구분자입니다. 파일 하나의 앞머리는 클론 없이도 읽을 수 있습니다.
contents API는 클론 없이 디렉터리 이름 15개를 돌려줬고, raw URL로는 지정한 SKILL.md의 앞머리를 읽었습니다. 반면 skills/SKILL.md처럼 경로를 잘못 짚으면 Not Found이고, 보조 파일 수와 본문 단어는 목록만 봐서는 알 수 없습니다.
writing-skills가 적은 “Start with “Use when…””과 이번 트리는 한 곳에서 어긋납니다. description이 Use when으로 시작하는 스킬은 14개이고, brainstorming만 “You MUST use this…”로 시작합니다. 글자 수만 보면 15개의 desc_len은 79에서 375입니다. 그 파일이 적은 500자 선호보다 짧고, 같은 파일의 1024는 앞머리 전체 문장으로 남아 있습니다. 이번 집계는 description 길이만 재었습니다.
시각은 세 칸으로 남아 있습니다. API의 pushed_at, 클론의 git log 시각, API의 updated_at은 출력에 서로 다른 필드로 있습니다. 커밋 시각과 pushed_at을 한 칸으로 합치지 않고, updated_at을 그 둘의 다른 이름이라고 부르지도 않습니다. 필드 이름 그대로 두는 편이 맞습니다.
저장소가 실은 구조 검사는 diagnosing-superpowers만 실행했습니다. 명령은 bash sp/tests/diagnosing-superpowers/test-skill-structure.sh이고, 해결 스크립트가 만들어 둔 sp 트리를 사용합니다. 첫 줄은 diagnosing-superpowers structure이고, 마지막 요약은 Passed: 46 Failed: 0이며, structure_test_exit는 0입니다. 통과 줄에는 SKILL.md 존재, frontmatter name, description의 Use when 접두어, description under 1024 characters, 본문 “SKILL.md body within 1000 words (991)”, ## Hard rules, ## Red Flags, 참조 파일과 기대 파일이 있습니다. 집계의 diagnosing-superpowers body_words도 991입니다. 이 1000단어 줄은 그 스킬의 검사입니다. subagent-driven-development의 body_words는 4854라 그 기준 밖에 있습니다. 46건 통과를 라이브러리 전체의 단어 수 통과로 옮기지 않습니다.
버전 문자열은 이번 저장소 API 출력의 선택 필드에 없습니다. plugin_version 7.0.0은 plugin.json에 있습니다. API description과 plugin description은 위에서 인용한 대로 문장이 다릅니다. 스킬 구조는 마크다운과 보조 파일이므로 공개 클론과 앞머리 읽기로 절차가 끝납니다.
저장소가 개편되면 수집 경로도 다시 찾아야 한다
이번 출력의 plugin_version 7.0.0은 .claude-plugin/plugin.json에서 읽었습니다. 저장소 API의 description과 플러그인 description도 서로 다르니, 자동 수집 설정에는 버전을 어느 파일에서, 설명을 어느 필드에서 읽는지 적어 둡니다. 구조 검사에는 references/claude-code-sessions.md, references/codex-sessions.md, references/other-harnesses.md가 제거되어 없다는 통과 줄도 있습니다. 이런 경로를 설정에 박아 둔 수집기라면 저장소가 갱신될 때 실제 참조 파일과 대조해 설정을 고쳐야 합니다.
헬스체크를 저장소 API의 HTTP 200에만 걸어 두면, 이번처럼 파일 경로가 404인데도 수집 성공으로 넘어갑니다. 수집 단계에서는 default_branch와 contents/skills로 현재 경로를 찾고, 파일을 받은 뒤에는 SKILL.md 앞머리와 필요한 참조 파일을 검사하도록 두 단계를 나누는 구성을 권합니다. 컨테이너 서비스의 준비 상태 검사도 외부 저장소를 매번 부르기보다, 검증을 마친 로컬 파일을 서비스가 읽을 수 있는지에 맞추는 쪽이 목적에 맞습니다. 배포 기록에는 플러그인 버전과 함께 branch_sha, 클론의 rev-parse HEAD를 남겨, 경로와 내용이 어느 트리에서 왔는지 이어 둡니다.
저장소 API, contents API, depth 1 클론이 각각 답하는 질문
결과를 필요한 파일 범위에 따라 나눠 봤습니다. 저장소 식별, 디렉터리 목록, 특정 파일의 앞머리, 전체 트리의 참조 관계는 조회해야 할 대상이 저마다 다릅니다. 표에서 성공 값과 실제로 얻은 내용을 나란히 보면, 목록 조회가 성공한 것을 본문 검증까지 끝난 것으로 착각하는 일이 줄어듭니다.
| 확인 | 이번 결과 | 스킬 파일 |
|---|---|---|
저장소 API clone_url과 branches/main | repo_http=200, SOLVED clone_url_matches_full_name=yes, SOLVED branch_name=main | 클론 주소와 기본 브랜치 |
https://github.com/obra/superpowers depth 1 클론 | clone_exit=0, 브랜치 main, SOLVED skill_md_count=15 | SKILL.md 15개와 보조 파일 |
contents/skills 목록 | contents_http=200, SOLVED contents_dir_count=15 | 디렉터리 이름 15개 |
raw writing-skills SKILL.md | raw_http=200, name: writing-skills | 그 파일의 앞머리 |
| diagnosing-superpowers 구조 검사 | Passed: 46 Failed: 0, structure_test_exit=0 | 그 스킬의 검사만 |
저장소가 obra/superpowers인지, 라이선스가 MIT인지, 기본 브랜치가 main인지만 알면 된다면 저장소 API 한 번이면 충분합니다. 이번 응답도 그 셋을 줬습니다. 다만 skills 키는 없으니 그 응답을 스킬 목록으로 쓰면 안 됩니다. 별 수와 시각이 필요하면 같은 출력의 stargazers_count, pushed_at, updated_at을 보면 됩니다.
디렉터리 이름만 필요하다면 contents API가 가장 가볍습니다. 이번 contents_dir_count는 15였고 타입은 모두 dir였습니다. 특정 SKILL.md의 앞머리만 볼 때는 raw URL로 그 파일만 받습니다. writing-skills가 그렇게 raw_http=200으로 왔습니다.
앞머리 15개, 본문 단어, 보조 파일, plugin.json의 7.0.0까지 필요하다면 full_name과 같은 주소로 depth 1 클론을 받는 쪽을 택하겠습니다. -b 없이 받아도 이번 브랜치는 main이었고, SOLVED skill_md_count=15와 find의 15가 맞아떨어졌습니다. API description과 plugin description은 문장이 다르므로, 인용할 때는 어느 필드인지 이름을 남깁니다.
클론 주소는 저장소 API의 clone_url을 그대로 쓰는 편이 안전합니다. 이번 clone_url은 full_name으로 만든 https://github.com/obra/superpowers.git와 같았고, branches/main의 branch_name도 main, branch_sha는 클론의 rev-parse HEAD와 같았습니다. 구조 검사는 이번 트리의 diagnosing-superpowers 스크립트만 돌렸습니다. 46건 통과는 그 스킬의 참조 파일과 1000단어 줄까지 포함한 결과이고, 15개 앞머리 집계와는 범위가 다릅니다.
파일을 읽는 기준으로는, description 14개가 Use when으로 시작하고 brainstorming 하나만 다르며, 보조 파일이 0인 스킬은 6개입니다. 그 밖의 디렉터리는 tops 줄에 SKILL.md 밖의 최상위 이름이 있습니다. using-superpowers만 H1이 -이고 첫 본문 줄이 <SUBAGENT-STOP>입니다.
ainewsnote가 쓴 글이고, 작성과 검증 절차는 콘텐츠 작성 및 검증 원칙 페이지에 공개해 두었습니다. 틀린 내용을 발견하셨다면 글 주소를 적어 연락처로 보내 주세요. 덧붙이면 본문의 Jesse Vincent는 plugin.json이 author로 적어 둔 이름입니다.
해결 스크립트로 클론을 받아 집계한 결과를 놓고 보면 스킬을 고쳐서 팀에 배포하는 용도로는 depth 1 클론으로 본문과 보조 파일을 함께 확보하는 구성이 제일 다루기 편했습니다. 이름만 보여 주는 목록 화면은 contents API로 만들고, 특정 스킬의 앞머리만 필요할 때는 raw 파일 조회를 쓰겠습니다. 운영에서는 내려받기와 검증을 배포 단계에 두고, 검증을 통과한 트리만 서비스에 넘기겠습니다. 실시간 요청을 받는 서비스라면 요청마다 저장소를 클론하지 말고 준비된 로컬 파일을 읽게 하고, 외부 저장소 갱신은 별도 작업으로 떼어 내기를 권합니다. 갱신할 때는 커밋 해시와 참조 파일 검사 결과를 같이 남겨, 목록은 멀쩡한데 본문 경로만 깨지는 상황을 따라갈 수 있게 하겠습니다. 이번 기록에서 배포 판단에 실제로 쓸모 있었던 것은 저장소의 별 수가 아니라 파일 경로, 같은 커밋의 트리, 적용 범위가 분명한 구조 검사 결과였습니다.






