agent-stocktake — 에이전트 정의 품질 감사
<harness>/agents/*.md 아래의 모든 에이전트 정의를 평가하고 각각에 대한 평결을 할당합니다:
유지(Keep) / 개선(Improve) / 업데이트(Update) / 병합(Merge) / 스킬로 강등(Demote to skill) / 해산(Dissolve) / 폐기(Retire). 감사 단위는 파일이지만, 비용 단위는 두 계층으로 분할됩니다 — 이 분할이 바로 이 스킬이 스킬-스톡테이크 및 규칙-스톡테이크 옆에 세 번째 형제로 존재하는 이유입니다.
설계 노트 — 하이브리드 비용 모델. 스킬의 비용은 트리거 오염(확률적 발동은 선택을 저하시킴)입니다; 규칙의 비용은 상주(항상 로드됨)입니다. 에이전트는 두 가지를 동시에 가지고 있습니다:
description은 "사용 가능한 에이전트 유형" 목록을 통해 모든 세션에 주입되며(상주, 규칙과 유사), 본문은 에이전트가 호출될 때만 로드됩니다(호출, 스킬과 유사 — 하지만 Claude의 위임 판단에 의해 트리거되며, 사용자의 단어에 대한 설명 일치로 트리거되지 않음). 따라서 설명은 상주 밀도로 감사되고 본문은 호출 품질로 감사되며, 각 계층에 대해 다른 질문이 적용됩니다.
설계 노트 2 — 승인된 편집은 이 스킬 자체에 의해 apply 브랜치에 적용됩니다. rules-stocktake와 동일한 이유입니다: 에이전트 정의에 대한 개선 엔진이 존재하지 않고, 코퍼스가 하나의 컨텍스트에서 읽을 수 있을 만큼 작으므로 — 위임은 과잉 설계가 됩니다. 유일한 예외 핸드오프는 스킬로 강등(스킬 생성은 스킬 작성자의 작업)입니다.
루트. 아래 <harness>는 이 감사가 다루는 환경입니다: 기본적으로 ~/.claude, 또는 스킬 harness-stocktake가 프로젝트 모드(루트 섹션에서 경로를 매핑하고, 해당되지 않는 증거를 명명하며, 어떤 규칙 검사가 적용되는지 설명)로 실행될 때 저장소의 .claude/입니다. 원장 <ledger>는 해당 환경에 속합니다: 이 스킬의 디렉토리가 <harness> 내부에 있을 때 ${CLAUDE_SKILL_DIR}/results.json(환경은 자체 스킬 사본을 가지고 있음), 그렇지 않으면 <harness>/stocktake/agent-stocktake.json(설치된 플러그인 또는 프로젝트 모드)입니다. 어느 쪽이든 apply 브랜치의 다른 변경사항과 함께 커밋됩니다.
모드 ($ARGUMENTS)
| 인자 | 동작 |
|---|---|
없음 / full | 모든 에이전트 정의를 읽고 평가 (기본값) |
changed | 수정 시간이 <ledger>의 evaluated_at보다 최신인 파일만 재평가하고, 나머지는 ledger에서 가져옴 |
changed는 변경사항을 인라인으로 감지합니다(스크립트 없음):
아래 경로는
${CLAUDE_SKILL_DIR}에서 시작하며, 이는 이 SKILL.md가 있는 디렉토리입니다; 변수를 대체하지 않는 에이전트는 그 디렉토리로 읽습니다.
find <harness>/agents -name "*.md" -newermt "$(jq -r .evaluated_at "<ledger>")"
rules-stocktake에서와 마찬가지로, 1단계 무결성 검사는 항상 전체 세트에 대해 실행됩니다 — 스킬 또는 후크를 폐기하면 수정되지 않은 에이전트 본문 내부의 참조가 조용히 깨지며, mtime은 이를 감지할 수 없습니다. 아래 3단계에서 참조 검사가 새롭게 실패하는 모든 에이전트는 재평가 세트에 추가됩니다. 스크립트에는 changed 모드도 없으며, 다른 이유가 있습니다: 거의 중복된 쌍과 상주 총계는 설정 속성이므로, 부분 코퍼스는 더 적은 수가 아닌 잘못된 수를 제공합니다.
1단계 — 증거, 인벤토리, 사용량
1단계 — 증거 스크립트 실행 (수동으로 계산하지 않음)
uv run --frozen --project "${CLAUDE_SKILL_DIR}" \
--directory "${CLAUDE_SKILL_DIR}" \
python scripts/agent_evidence.py --root <harness>
증거 모드: stdout에 JSON, 종료 코드 0 (찾은 내용 수에 관계없이, 종료 코드 2는 코퍼스를 읽을 수 없는 경우에만). 측정하고 열거합니다. 절대 평결을 할당하지 않습니다.
JSON을 결과로 기록하세요 — 눈으로 다시 측정하지 마세요. 제공하는 내용:
| JSON 필드 | 대체 |
|---|---|
desc_words / body_lines / total_desc_words | wc -w / wc -l 패스 |
tools.items[].status + registry.known_tools | builtin / unverified / mcp. unverified는 "스크립트의 날짜 목록에 없음"을 의미하며, "존재하지 않음"이 아닙니다 — 당신은 실시간 도구 레지스트리를 가지고 있고 스크립트는 그렇지 않으므로, Update 증거로 판단하기 전에 자신의 도구 목록과 unverified를 확인하세요. registry.known_tools.as_of는 해당 목록의 날짜입니다(--known-tools로 대체된 경우 null) |
tools.items[].server_in_config + registry.mcp_config_files | 평결이 아닙니다. 나열된 로컬 구성 파일에 서버가 나타나는지만 알려줍니다 — 커넥터에서 제공하는 서버는 거기에 전혀 나타나지 않으므로, false가 "폐기"를 의미하지는 않습니다. null은 구성 소스를 구문 분석하지 않았음을 의미합니다. 이것을 전혀 사용하기 전에 각 소스의 status를 확인하세요; ok가 아닌 소스 옆의 false는 증거가 아닙니다 |
unreadable | 스캔이 열 수 없는 파일과 이유입니다. 비어 있지 않으면 스캔이 불완전함을 의미합니다 — 이름을 지정하고 총계를 말하기 전에 수정하세요. 읽을 수 없는 파일 내부의 억제 지시문이 이제 없음으로 인증되었기 때문입니다 |
tools_unparsable | tools: 줄을 구문 분석하지 못한 에이전트입니다(예: YAML 흐름 스타일). 해당 items는 []이며, 이는 tools: 키가 없는 에이전트(즉, 제한 없음)와 동일하게 보입니다. 그렇지 않습니다. 목록이 단순히 읽히지 않은 것입니다 |
description_near_duplicates | 거의 동일한 설명 검사입니다. 쌍은 위임 트래픽을 분할하므로, 각 쌍은 1단계 중복 질문에 제공됩니다 |
suppression_candidates | 1단계 억제 질문을 위한 줄 번호가 있는 후보입니다(카탈로그는 코퍼스가 이중 언어이므로 이중 언어입니다) |
always_never_candidates | 과잉 제약 질문을 위한 줄 번호가 있는 후보입니다, 본문만 해당 — 설명은 위임 트리거이지 에이전트에 대한 지시가 아닙니다 |
마지막 두 개는 결과가 아닌 후보입니다 — 각 인용된 줄을 기록하기 전에 읽으세요. 카탈로그는 또한 최소값이지 전체 목록이 아닙니다: 하나의 코퍼스에서 가져온 다섯 가지 표현을 보유하므로, 다른 방식으로 작성된 억제("확실하지 않은 것은 건너뛰기", "노이즈가 될 결과는 피하기")는 어떤 JSON 필드에도 나타나지 않습니다. 계속 읽으세요. 빈 목록이 깨끗한 진단서가 아닙니다.
측정된 거짓 양성 비율은 이 파일 외부에 있으므로, 이 단계는 유효기간이 지난 숫자를 전달하지 않습니다.
2단계 — 나머지를 이미 소유하고 있는 게이트 읽기
lint 게이트(<harness>/scripts/hooks/harness_lint.py)가 있는 환경에서는 해당 게이트가 소유하므로 세 가지 검사가 스크립트 외부에 남아 있습니다. 출력이 있을 때 읽고, 없으면 Read/Grep으로 세 가지 검사를 수행합니다:
- 프론트매터가 구문 분석되고
name,description,origin,model을 포함하며,model이 별칭인 경우 → lint 스크립트의lint_agents(저자 환경에서는.claude/verify.sh및 pre-commit 후크에서 실행됨). 환경이 원본 규칙을 명시하는데 원본이 누락된 경우, 여전히 무결성 결과이며 Improve에 제공됩니다. -
name이 파일 이름 줄기와 같음 → 동일한lint_agents. 판단을 허용하지 않으며 위임 레지스트리가 이를 기준으로 키를 설정하므로, 커밋을 중지하는 게이트에 속합니다 — 판독기가 기록할 수도 있고 기록하지 않을 수도 있는 증거가 아닙니다. 여기서 발견된 내용은 차단된 커밋이지 스톡테이크 평결이 아닙니다. - 에이전트 본문 내부의 Markdown 링크 확인 → 동일한 스크립트의
lint_markdown_links(범위에agents포함).
3단계 — 베어 경로 참조 확인 (다른 어떤 것도 소유하지 않은 유일한 검사)
lint_markdown_links는 링크 구문만 보고, skill-health의 scan_refs.py는 스킬 트리를 스캔합니다. 에이전트 본문 내부에 산문으로 작성된 경로(<harness>/hooks/foo.sh, skills/foo/SKILL.md)는 어느 쪽에도 포함되지 않습니다. 코퍼스에서 이를 Grep하고 각각 ls로 확인합니다.
4단계 — 인벤토리
Glob으로 열거: <harness>/agents/*.md. 모든 파일을 하나의 컨텍스트로 읽습니다(코퍼스는 작습니다). 상주 및 본문 열은 1단계 JSON에서 가져옵니다 — 문서(이 문서 포함)에 기록된 수치를 절대 신뢰하지 마세요.
5단계 — 사용량 카운트
증거 입력: 낮은 사용량이 후보를 지명하고, 내용이 평결을 결정합니다. 설정이 에이전트 사용 로그(<harness>/metrics/agent-usage.jsonl; 없으면 사용량 n/a)를 유지하는 경우, 인라인으로 읽습니다(사용량 로깅 후크(예: log-agent-usage.sh)는 Agent 도구 실행당 하나의 invoke 이벤트를 추가하며, agent 필드(값은 subagent_type)로 키가 지정됨)하고 7/30/90일 동안 에이전트별 이벤트를 계산합니다. 일회성 python3/jq one-liner로 집계합니다.
- 로그가 없거나 첫 번째 이벤트가 90일보다 이전인 경우, 사용량을
—(측정되지 않음)으로 렌더링합니다. 절대 0으로 렌더링하지 마세요 — 측정되지 않음과 사용되지 않음은 다른 사실입니다. - 카운트는 하한입니다: Agent(Task) 도구 실행만 캡처됩니다. Workflow
agent()워커, 플러그인 내부 디스패치, 도구 호출을 우회하는 내장 메커니즘은 후크에 도달하지 않습니다. 낮은 사용량 에이전트는 1단계에서 Retire/Dissolve 후보로 진입합니다. 그 가치는 일시적일 수 있으므로(예: 논문 리뷰어는 기탁 근처에서만 실행됨), 내용이 결정합니다. - 로그는 2026-07-27 이후부터 존재합니다. 그 이전 날짜에는 측정이 전혀 없습니다.
결과를 먼저 명시하고, 1단계 JSON에서 카운트를 가져옵니다: agents_total 에서 unreadable의 항목(이름을 지정해야 함)을 뺀 값, total_desc_words(목록의 세션당 상주 비용), 무결성 실패, 사용량 측정 가능 여부. 실패를 사전 계산된 증거로 1단계에 전달합니다.
2단계 — 평가 (완전 인라인, 전체적)
전체 세트를 보면서 모든 본문을 읽습니다.
1단계 — 이진 스크리닝 (모든 에이전트). 항목당 명시적 예/아니오. 아니오 답변만 표시합니다. 처음 두 질문은 설명 계층(상주)을 감사하고, 나머지는 본문 계층(호출)을 감사합니다:
- 설명이 상주 비용을 정당화하는가? — 항상 로드되는 목록에서 밀집되고, 고유하며, 선택을 가능하게 함. 위임할 때와 위임하지 말아야 할 때를 명시
- 설명이 본문에 대해 진실한가? — 약속하는 것이 본문이 하는 것과 일치함(표류된 설명은 본문이 괜찮더라도 모든 세션에서 위임을 잘못 지정함)
- 본문에 억제 지시문이 없는가? — 이 에이전트의
suppression_candidates에서 1단계 JSON의 항목부터 시작하여 Step 1당 처리. 해당되는 것: 신뢰도 임계값("N% 이상 확신하는 결과만 보고", "각 결과에 신뢰도를 할당하고 낮은 것은 폐기"), 심각도 기준("높은 심각도만"), "보수적" 프레이밍. 현재 세대 지침은: 모든 것을 보고, 별도의 패스에서 필터링하라 — 억제 지시문은 문자 그대로 따르며 조용히 결과를 떨어뜨림. 여기서 아니오는 반전에 의한 개선(Improve-by-inversion) 후보: 반대 방향으로 지시문을 다시 작성하고, 절대 섹션을 삭제하지 마십시오(삭제는 억제 프레임을 남김; 반전은 대체함) - 외부 원본 사본이 최신인가? —
origin이 외부 저장소이고 본문이 있는 그대로의 사본(예:Explore와 같은 내장 재정의)인 에이전트의 경우, 본문의 선행 주석에 명명된 소스 버전이 설치된 도구(claude --version)와 일치함. 불일치는 Update입니다(번들과 비교하여 다시 복사). 이러한 사본에 대해서는 억제 및 과잉 제약 질문을 건너뜀 — 본문은 공급업체의 것임 - 본문에 이전 세대의 과잉 제약이 없는가? — 현재 모델이 기본적으로 보유한 판단, 반복 강조, 주변 컨텍스트 판단이 소유해야 하는 ALWAYS/NEVER 쌍에 대한 완전한 단계별 절차. 1단계의
always_never_candidates는 토큰을 찾음. 적중이 지시문인지 아니면 단순히 단어를 인용하는지 결정하는 것이 이 질문의 작업 - 기반 환경(substrate)에 흡수되지 않았는가? — 환경이 이제 이 에이전트의 작업을 기본적으로 커버하는가(기본 리뷰 메커니즘, 계획 모드, 내장 슬래시 명령)? 흡수 → Dissolve 후보. 주장은 흡수자를 구체적으로 명명해야 함. 신선함/풍부한 컨텍스트 축으로 판단: 신선한 컨텍스트(리뷰, 적대적 검증, 본질 평가 — 제안자의 매몰 비용과의 상관 해제)로부터 이점을 얻는 역할은 서브에이전트에 합법적으로 존재함; 풍부한 컨텍스트(계획, 생성, 구현 — 사용자 의도, 대화 내 제약)로부터 이점을 얻는 역할은 메인 루프 작업이므로, 메인 루프 자체가 흡수자로 간주됨. 두 가지 보조 논거가 합법적으로 풍부한 컨텍스트 당김을 재정의합니다: 고정 입력 렌더링 계약 — 호출자가 호출 전에 자체 포함된 패킷을 고정하므로 대화 컨텍스트가 설계상 필요하지 않음(프롬프트 수집기, 의도적으로 한 줄 받음) — 및 대량 컨텍스트 격리 — 작업이 메인 컨텍스트를 오염시킬 양을 읽거나 생성함(리서처, 실행당 하나의 웹 연구 각도)
- 기술적 참조가 최신인가? — 본문이 명명하는 모든 아티팩트를 무조건 확인합니다: 각 참조 경로
ls, 명명된 명령에 대해--help/ 버전 확인, 플래그, 모델 이름 및 도구 목록 실행. "오래된 것처럼 보이면 확인"은 금지된 표현입니다 — 조건이 희석시키는 것입니다. 결정론적으로 확인 가능한 주장은 결정론적으로 확인하세요, 매번. - 세트 내에서 고유한가? — 다른 에이전트(또는 스킬)가 동일한 작업을 소유하지 않음. 문서화된 오케스트레이터→서브에이전트 분할은 중복이 아님
8개의 질문이며 더 이상 없음 — 추가 분해는 전체적 판단을 저하시킵니다(참조 참조).
2단계 — 평결 압력 테스트(Keep이 아닌 후보만). 1~3개의 에이전트별 원자적 예/아니오 질문을 생성하여 초안 평결을 반증하려고 시도하며, 각 질문은 한 줄의 증거(파일 읽기, 경로 확인, --help, WebSearch, 환경 문서 확인)로 답변됩니다. Dissolve 후보의 경우 한 질문이 필수입니다: "흡수하는 환경 기능을 구체적으로 명명할 수 있습니까 — 예/아니오" — 흡수자를 명명할 수 없는 흡수 주장은 반증됩니다. 그리고 Dissolve가 기반 환경 대응물이 부족한 기능(도구, 유선 서브에이전트)에 의해 반증되려고 할 때, 반증을 수락하기 전에 한 가지 반대 질문이 필수입니다: "해당 기능을 사용하기에 서브에이전트가 적절한 장소입니까 — 아니면 메인 루프가 여전히 그것을 보유하고 있습니까?" 기능 존재는 필요 조건이지만 충분 조건은 아닙니다. 신선함/풍부한 컨텍스트 축이 기능이 속한 위치를 결정합니다(선례: 플래너 반증은 메인 루프가 전체 Agent 도구를 보유하고 있기 때문에 붕괴됨). Keep 바운드 에이전트는 동적 질문을 받지 않습니다.
평가는 *
콘텐츠가 잘려서 나머지는 생략되었습니다.
3단계 — 적용. 승인된 편집을 apply 브랜치에 기록하고, ledger 파일의 evaluated_at 및 version을 업데이트합니다.