@uzysjung/agent-harness 26.141.1 → 26.143.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -15,7 +15,7 @@ import {
15
15
  init_esm_shims,
16
16
  residentCost,
17
17
  resolveBundleRoot
18
- } from "./chunk-5RWSZT2J.js";
18
+ } from "./chunk-7KAPL2PZ.js";
19
19
 
20
20
  // src/trust-tier-drift.ts
21
21
  init_esm_shims();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uzysjung/agent-harness",
3
- "version": "26.141.1",
3
+ "version": "26.143.0",
4
4
  "description": "Curate vetted AI-coding skills & plugins by your tech stack — install only what you need, across Claude Code, Codex, OpenCode & Antigravity",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -41,4 +41,10 @@
41
41
 
42
42
  현행 SSOT 만 루트/`docs/` 에 둔다. 히스토리(옛 버전·폐기 sub-spec·완료된 audit 산출물)는 `docs/archive/` 로 격리하고 지도 파일(`archive/README.md`) 하나로 찾게 한다. 현행 문서에 히스토리가 쌓이면 "지금 무엇이 맞는지" 읽는 비용이 히스토리에 비례해 커진다.
43
43
 
44
- **why 중심으로 쓴다** — what 은 코드와 diff 가 보여준다. SPEC 이 800줄을 넘으면 기능별로 분리한다(`spec-scaling` 스킬). **본 규약을 무는 게이트는 기본 제공되지 않는다** — 같은 drift 가 반복되면 프로즈를 늘리지 말고 게이트를 만들어라.
44
+ **why 중심으로 쓴다** — what 은 코드와 diff 가 보여준다. SPEC 이 800줄을 넘으면 기능별로 분리한다(`spec-scaling` 스킬).
45
+
46
+ ## 검증 게이트
47
+
48
+ `.uzys-agent-harness/spec-drift-check.sh`(전 트랙 설치)가 SPEC/TODO 의 미완 잔존·Status 불일치를 검출한다 — **훅이 아니라 명시 호출**이다: 인자 없이 부르면 경고(exit 1), `ship` 인자면 차단(exit 2). 백로그는 `ship-gate:ignore` 구간만 면제된다(기본값은 검사 · 표식 짝이 깨지면 면제를 통째로 무시).
49
+
50
+ **한계를 알고 써라**: 탐지 경로(`docs/SPEC.md`·`docs/todo.md` 등) 밖의 문서 레이아웃은 게이트가 못 본다. 그 경우 본 규약은 프로즈로만 작동한다 — 안 지켜져도 아무도 안 막는다. 그게 확인되면 **게이트를 넓혀라, 프로즈를 늘리지 말고.**
@@ -8,7 +8,7 @@ ship 단계 실행 시 아래 모든 항목을 통과해야 배포 가능.
8
8
  - [ ] **커버리지 기준 충족**: `test-policy.md` 의 Track 별 threshold 확인. **전체 실행(full)을 요구하는 지점은 여기다** — 커밋에는 테스트가 없고 머지에는 영향 범위만 돈다(시점별 정책은 `test-policy.md` 가 SSOT)
9
9
  - [ ] **CI green 이 배포의 전제로 배선됐는지**: 릴리스가 CI job 에 `needs:` 로 묶여 **CI red 면 게시가 안 일어나야** 한다. 게시가 CI 와 별개 워크플로면 red 를 통과해 나간다
10
10
  - [ ] **Security Scan · 의존성 감사**: `npx ecc-agentshield scan` 결과 CRITICAL/HIGH 없음 · `npm audit`(Node.js) 또는 `pip-audit`(Python) critical/high 취약점 없음
11
- - [ ] **SPEC/PRD 정합성**: 이번 사이클에 하기로 한 항목이 미완으로 남아 있지 않은가 (`doc-governance.md` "작업 완료 처리")
11
+ - [ ] **SPEC/PRD 정합성**: 이번 사이클에 하기로 한 항목이 미완으로 남아 있지 않은가 (`doc-governance.md` "작업 완료 처리"). 실행 확인 = `bash .uzys-agent-harness/spec-drift-check.sh ship` — **exit 2 면 차단**(동기화 후 재시도), exit 1 은 경고, 0 이 통과. 백로그는 `ship-gate:ignore` 구간으로 감싸 면제한다(표식이 안 닫히면 면제를 통째로 무시)
12
12
  - [ ] **Review 게이트 통과**: review 단계에서 CRITICAL 이슈 없음 확인
13
13
 
14
14
  ## Post-Ship
@@ -0,0 +1,131 @@
1
+ #!/bin/bash
2
+ # spec-drift-check.sh
3
+ # SPEC/TODO 문서의 drift를 검출한다. Verify 또는 Ship 단계에서 호출 가능.
4
+ #
5
+ # 훅이 아니라 **명시 호출** 스크립트다 — 자동 발화 경로는 없다.
6
+ # bash .uzys-agent-harness/spec-drift-check.sh # 경고 수준 (verify)
7
+ # bash .uzys-agent-harness/spec-drift-check.sh ship # 차단 수준 (ship)
8
+ # 호출자 = `ship-checklist.md` SPEC/PRD 정합성 항목 · `doc-governance.md` 검증 게이트 절.
9
+ #
10
+ # 검출 항목:
11
+ # 1. SPEC 문서에 unchecked 항목 존재 (docs/SPEC.md → SPEC.md 중 첫 존재 파일)
12
+ # 2. TODO 문서에 unchecked 항목 존재 (docs/todo.md → docs/TODO.md → todo.md → TODO.md
13
+ # → tasks/todo.md 중 첫 존재 파일 — first-match 라 대소문자 무시 FS 에서도 이중 카운트 없음)
14
+ # 3. SPEC Status가 "Define"인데 build/verify gate가 완료된 경우 (gate-status.json 존재 시)
15
+ # 4. Ship 단계에서는 모든 unchecked가 차단
16
+ #
17
+ # Exit codes:
18
+ # 0: drift 없음
19
+ # 1: drift 발견 (경고 출력)
20
+ # 2: 차단 수준 drift (Ship 게이트에서 차단)
21
+ set -e
22
+
23
+ PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}"
24
+ DOCS_DIR="$PROJECT_DIR/docs"
25
+ [ ! -d "$DOCS_DIR" ] && DOCS_DIR="$PROJECT_DIR/Docs"
26
+
27
+ DRIFT=0
28
+ BLOCK=0
29
+
30
+ # unchecked 항목을 센다. 단 `ship-gate:ignore` 구간은 제외한다 — 게이트가 잡으려는 것은 "이번
31
+ # 사이클에 하기로 해놓고 안 한 것"이지 "언젠가 할 일(백로그)"이 아니다. 백로그는 정상적으로 항상
32
+ # 존재하므로 구분 없이 세면 게이트가 상시 차단이 되고, 그러면 사람들은 체크박스를 안 쓰는 쪽으로
33
+ # 우회한다 → 셀 게 없어 게이트가 죽는다.
34
+ #
35
+ # 기본값은 검사다. 면제는 표식이 있는 쪽이어야 한다. 표식이 짝이 안 맞으면 면제를 통째로
36
+ # 무시한다(fail-closed) — 표식 하나 잘못 써서 게이트가 통째로 꺼지는 경로를 만들지 않는다.
37
+ #
38
+ # 표식은 **단독 줄**일 때만 인정한다(앞뒤 공백만 허용). 그래야 이 기능을 설명하는 산문이
39
+ # — 예: 백틱으로 인용한 사용법 — 파서에 진짜 표식으로 잡히지 않는다. 기능을 그 기능이
40
+ # 적용되는 파일 안에서 문서화할 수 없으면 안 된다. (실제로 그 함정을 밟고 추가한 규칙이다.)
41
+ count_unchecked() {
42
+ local file="$1"
43
+ awk '
44
+ /^[ \t]*<!--[ ]*ship-gate:ignore-start[ ]*-->[ \t]*$/ { skip = 1; opened++; next }
45
+ /^[ \t]*<!--[ ]*ship-gate:ignore-end[ ]*-->[ \t]*$/ { skip = 0; closed++; next }
46
+ /^- \[ \]|^ - \[ \]/ { total++; if (!skip) counted++ }
47
+ END {
48
+ if (opened != closed) {
49
+ printf "spec-drift-check: ship-gate:ignore 표식 불일치 (start %d / end %d) — 면제를 무시한다\n", opened, closed > "/dev/stderr"
50
+ print total + 0
51
+ } else {
52
+ print counted + 0
53
+ }
54
+ }
55
+ ' "$file"
56
+ }
57
+
58
+ # 후보 목록 중 첫 존재 파일 (없으면 빈 문자열). docs/ 고정이 아니라 실제 워크플로
59
+ # 산출 레이아웃(root SPEC.md·tasks/todo.md 등)까지 훑는다.
60
+ first_existing() {
61
+ local f
62
+ for f in "$@"; do
63
+ if [ -f "$f" ]; then
64
+ echo "$f"
65
+ return 0
66
+ fi
67
+ done
68
+ echo ""
69
+ }
70
+
71
+ SPEC_FILE=$(first_existing "$DOCS_DIR/SPEC.md" "$PROJECT_DIR/SPEC.md")
72
+ TODO_FILE=$(first_existing "$DOCS_DIR/todo.md" "$DOCS_DIR/TODO.md" "$PROJECT_DIR/todo.md" \
73
+ "$PROJECT_DIR/TODO.md" "$PROJECT_DIR/tasks/todo.md")
74
+
75
+ # 1. SPEC unchecked 검사
76
+ if [ -n "$SPEC_FILE" ]; then
77
+ UNCHECKED=$(count_unchecked "$SPEC_FILE")
78
+ UNCHECKED=${UNCHECKED:-0}
79
+ if [ "$UNCHECKED" -gt 0 ] 2>/dev/null; then
80
+ echo "DRIFT: ${SPEC_FILE#"$PROJECT_DIR"/}에 unchecked 항목 ${UNCHECKED}건" >&2
81
+ DRIFT=$((DRIFT + 1))
82
+ fi
83
+ fi
84
+
85
+ # 2. TODO unchecked 검사
86
+ if [ -n "$TODO_FILE" ]; then
87
+ UNCHECKED=$(count_unchecked "$TODO_FILE")
88
+ UNCHECKED=${UNCHECKED:-0}
89
+ if [ "$UNCHECKED" -gt 0 ] 2>/dev/null; then
90
+ echo "DRIFT: ${TODO_FILE#"$PROJECT_DIR"/}에 unchecked 항목 ${UNCHECKED}건" >&2
91
+ DRIFT=$((DRIFT + 1))
92
+ fi
93
+ fi
94
+
95
+ # 3. SPEC Status 일관성 — gate-status.json과 대조 (6-gate 워크플로 사용 프로젝트만; 파일 없으면 skip)
96
+ GATE_FILE="$PROJECT_DIR/.claude/gate-status.json"
97
+ if [ -f "$GATE_FILE" ] && [ -n "$SPEC_FILE" ] && command -v jq &> /dev/null; then
98
+ BUILD_DONE=$(jq -r '.build.completed // false' "$GATE_FILE")
99
+ VERIFY_DONE=$(jq -r '.verify.completed // false' "$GATE_FILE")
100
+
101
+ # SPEC Status가 "Define"인지 확인 (frontmatter 형식만, 본문 파이프라인 설명 제외)
102
+ if grep -qE "^> \*\*Status\*\*:.*Define" "$SPEC_FILE"; then
103
+ if [ "$BUILD_DONE" = "true" ] || [ "$VERIFY_DONE" = "true" ]; then
104
+ echo "DRIFT: SPEC Status='Define'인데 Build/Verify gate가 완료됨" >&2
105
+ DRIFT=$((DRIFT + 1))
106
+ # Ship 게이트에서는 차단 (Build 이후에도 SPEC이 Define이면 안 됨)
107
+ [ "$1" = "ship" ] && BLOCK=1
108
+ fi
109
+ fi
110
+ fi
111
+
112
+ # 4. Ship 단계에서는 모든 unchecked가 차단
113
+ if [ "$1" = "ship" ] && [ "$DRIFT" -gt 0 ]; then
114
+ BLOCK=1
115
+ fi
116
+
117
+ # Summary
118
+ if [ "$DRIFT" -eq 0 ]; then
119
+ echo "OK: SPEC/TODO 동기화 상태 정상"
120
+ exit 0
121
+ fi
122
+
123
+ if [ "$BLOCK" -eq 1 ]; then
124
+ echo "" >&2
125
+ echo "BLOCKED (ship gate): SPEC/TODO drift 발견 — 동기화 후 재시도" >&2
126
+ exit 2
127
+ fi
128
+
129
+ echo "" >&2
130
+ echo "WARNING: SPEC drift ${DRIFT}건. 동기화 권장." >&2
131
+ exit 1
@@ -0,0 +1,278 @@
1
+ ---
2
+ name: audit-harness-fit
3
+ description: >-
4
+ Audits whether a project's resident steering layer still earns the context it costs — the
5
+ CLAUDE.md/AGENTS.md anchor and its `@import` chain, `.claude/rules/`, hooks, permission rules,
6
+ and the skill descriptors preloaded every session. INVENTORY measures what actually loads;
7
+ EVIDENCE gathers block logs and correction history; VERDICT rules each section keep / rewrite /
8
+ relocate / delete against published authoring guidance instead of taste; RELOCATE moves
9
+ procedures into skills, must-hold guarantees into hooks and permission rules, and derivable
10
+ facts back into code or tests; APPLY proposes the edit and waits for approval. Use when the
11
+ user says any of "하네스 정리", "룰·훅이 밥값을 하는지", "CLAUDE.md 다이어트", "상주 컨텍스트
12
+ 정리", "룰이 너무 많아", "훅이 실제로 뭘 막고 있는지" — or the English equivalents "harness
13
+ audit", "trim my CLAUDE.md", "are my rules earning their keep", "prune the steering layer",
14
+ "why does Claude ignore my rules". Fires for both Korean and English phrasing. Do NOT use it to
15
+ escalate a countermeasure after a defect came back (that is recurrence-prevention), and not for
16
+ drift between the product's own docs and its code (that is audit-service-gaps DRIFT).
17
+ ---
18
+
19
+ # Audit Harness Fit (상주 조종층 감사)
20
+
21
+ The steering layer around an agent has a documented growth loop and no documented shrink loop.
22
+ The published guidance is explicit about when to add — *"Claude makes the same mistake a second
23
+ time"*, *"a code review catches something Claude should have known"* — and the natural response is
24
+ another line in CLAUDE.md, another rule file, another hook. Nothing in that loop ever fires in
25
+ reverse. So the layer grows monotonically until it hits the failure the same guidance names:
26
+ *"If your CLAUDE.md is too long, Claude ignores half of it because important rules get lost in the
27
+ noise."*
28
+
29
+ This skill is the reverse pass. It takes the resident layer as it is today, measures it, and rules
30
+ on each part with **three kinds of evidence only**:
31
+
32
+ 1. **Published criteria** — what the vendor documentation says belongs resident, what belongs
33
+ on-demand, and what is not an enforcement layer at all. Quotes and sources:
34
+ [references/official-criteria.md](references/official-criteria.md).
35
+ 2. **Block and correction logs** — what the enforcement layer actually stopped, and what the
36
+ human actually had to correct.
37
+ 3. **Measurement** — item counts and token estimates per surface, taken the same way twice so
38
+ before and after are comparable.
39
+
40
+ **Opinion is not one of the three.** "This rule feels important" and "this rule feels like bloat"
41
+ are the same evidence class, and a verdict that pits one against the other is a coin flip wearing
42
+ a report's clothes. If none of the three applies to a section, the verdict is `unjudged`, and it
43
+ stays exactly as it is.
44
+
45
+ ## When to use
46
+
47
+ - The resident layer has grown across many sessions and nobody has ever removed anything.
48
+ - Claude keeps violating a rule that is plainly written down — the diagnostic the docs give for
49
+ that symptom is *file length*, not rule wording.
50
+ - You are about to add another rule and want to know what the existing ones are doing first.
51
+ - A model upgrade landed and some instructions may now be scaffolding for a weakness that is gone.
52
+
53
+ Not for: a defect that just recurred — `recurrence-prevention` owns that, and it moves *one*
54
+ countermeasure up a ladder rather than re-judging the whole layer. Not for mismatch between the
55
+ product's documentation and the product's code — that is `audit-service-gaps` in DRIFT mode.
56
+
57
+ ---
58
+
59
+ ## Stage 1 — INVENTORY (what actually loads every session)
60
+
61
+ Enumerate the resident surfaces before judging any of them. Resident means loaded at session
62
+ start whether or not it gets used:
63
+
64
+ | Surface | Where it lives | Resident? |
65
+ |---|---|---|
66
+ | Project anchor | `CLAUDE.md` / `AGENTS.md` at the repo root (and parent directories) | Full text, every session |
67
+ | Project anchor 2 | `.claude/CLAUDE.md` — a second file, not an alias of the first | Full text, every session |
68
+ | User anchor | the same filenames under the home config dir (`$HOME/.claude/`) | Full text — a separate scope, not a parent directory |
69
+ | Imports | every `@path` reachable from any of those anchors, up to four hops | Full text — imports organize, they do not reduce |
70
+ | Auto-memory | `MEMORY.md`, written by the agent to itself | Full text; often the single largest item |
71
+ | Rules | `.claude/rules/*.md` | Full text if no `paths:` frontmatter; on match if scoped |
72
+ | Skills | `.claude/skills/*/SKILL.md` | **Name + description only**; the body loads on trigger |
73
+ | Subagents | `.claude/agents/*.md` | Description preloaded, same as skills |
74
+ | Hooks | `settings.json` hook entries + their scripts | **Zero**, unless the hook writes to stdout |
75
+ | Permissions | `permissions.allow` / `ask` / `deny` | Enforcement, not context |
76
+
77
+ Then measure. Use whatever the project already provides; if it provides nothing, plain shell is
78
+ enough and portable. Measure every row in the same unit — **bytes** — or the rows cannot be added
79
+ up, and "half the layer is rules" becomes a guess:
80
+
81
+ ```bash
82
+ have() { for f in "$@"; do [ -f "$f" ] && printf '%s\n' "$f"; done; }
83
+
84
+ # ⓐ every anchor that exists, plus one hop of @imports resolved next to the file that declared them
85
+ anchors=$(have CLAUDE.md .claude/CLAUDE.md AGENTS.md "$HOME/.claude/CLAUDE.md")
86
+ imports=$(for a in $anchors; do
87
+ grep -o '@[^[:space:])]*' "$a" |
88
+ sed -e "s|^@~|$HOME|" -e "s|^@/|/|" -e "s|^@|$(dirname "$a")/|"
89
+ done)
90
+ have $anchors $imports | xargs wc -c # ÷ 4 ≈ tokens
91
+
92
+ # ⓑ rules — count them, then size only the ones without `paths:` frontmatter
93
+ find .claude/rules -name '*.md' | wc -l
94
+ grep -L '^paths:' .claude/rules/*.md | xargs wc -c
95
+
96
+ # ⓒ auto-memory — one home dir holds every project's, so keep the ones naming this project
97
+ find . "$HOME/.claude" -name 'MEMORY.md' 2>/dev/null |
98
+ grep -e '^\./' -e "$(basename "$PWD")" | xargs wc -c
99
+
100
+ # ⓓ for skills and subagents the descriptor is the resident part — size the frontmatter, not the file
101
+ awk 'FNR==1{n=0} /^---$/{n++;next} n==1' .claude/skills/*/SKILL.md | wc -c
102
+ awk 'FNR==1{n=0} /^---$/{n++;next} n==1' .claude/agents/*.md | wc -c
103
+ ```
104
+
105
+ Three traps that make an inventory wrong rather than incomplete:
106
+
107
+ - **Two copies of the same name.** A repo that *ships* a harness has a development copy and a
108
+ distributed copy of the same filenames. Measure the one the session actually loads, and say
109
+ which one you measured.
110
+ - **`paths:` frontmatter changes the answer.** A rule with a `paths:` list is not resident; a rule
111
+ without one is. Read the frontmatter, do not assume.
112
+ - **Imports are not free.** Splitting a long anchor into `@imports` improves organization and
113
+ changes the resident total by nothing.
114
+
115
+ Record the numbers as bytes per surface plus one total. Every later claim about "smaller" has to
116
+ point back at them, and a total that quietly drops a surface makes every percentage after it wrong.
117
+
118
+ ## Stage 2 — EVIDENCE (what the layer actually did)
119
+
120
+ For each resident item, look for a trace that it did work:
121
+
122
+ - **Block logs.** Blocking hooks that append one line per block (`.uzys-agent-harness/hook-blocks.log`
123
+ where this harness is installed) give the only direct data on what enforcement actually caught.
124
+ - **Correction history.** `git log` on the steering files themselves, plus the commits that
125
+ *followed* a rule's introduction: was the mistake it targets absent afterward, or does it recur?
126
+ - **The maintainer's own record.** Issue threads, postmortems, memory files — a rule created after
127
+ a real incident has a citation; a rule created out of caution does not.
128
+
129
+ **A log with zero lines is not an acquittal.** It has two readings that data alone cannot separate:
130
+ nothing needed blocking, or the log was born last week / the hook never fired / the hook never wired
131
+ up. Report `no sample` and go find a second signal — when the hook was added, whether its matcher can
132
+ ever match, whether the file exists at all. A hook whose matcher cannot match anything is not
133
+ "quietly effective", it is dead wiring, and that is a finding in its own right.
134
+
135
+ The same asymmetry runs the other way: a log line proves the hook fired, not that the block was
136
+ *correct*. Read the blocked targets. A block on a path the maintainer intended to edit is a false
137
+ positive, and false positives are the cost side of the enforcement ledger.
138
+
139
+ ## Stage 3 — VERDICT (rule on each section, one at a time)
140
+
141
+ The unit is the **section**, not the file. Files are usually mixed — one paragraph carrying a real
142
+ project fact, three carrying things any competent model already does.
143
+
144
+ The primary question comes straight from the published guidance: **"Would removing this cause
145
+ Claude to make mistakes?"** If not, cut it. Two corollaries decide most sections without argument:
146
+
147
+ - Anything derivable from the codebase (directory layouts, dependency lists, file-by-file
148
+ descriptions, API documentation) is derivable *by the model, on demand*. It does not need to be
149
+ resident.
150
+ - Anything the model does correctly without the instruction is a no-op that still costs adherence
151
+ from the rules around it.
152
+
153
+ Then run the **generation lint**. Recent guidance names prompt patterns that were useful for older
154
+ models and now actively cost tokens or quality — they survive in steering layers as legacy
155
+ scaffolding, so look for them by name:
156
+
157
+ | Pattern to flag | Why it is now a cost |
158
+ |---|---|
159
+ | Explicit verification instructions ("add a final verification step", "use a subagent to verify") | The model verifies its own work unprompted; the instruction causes over-verification |
160
+ | Re-check instructions ("double-check your answer", "re-verify before responding") | Compounds with behavior the model already has — cost without quality |
161
+ | Severity suppression in review prompts ("only report high-severity issues", "be conservative") | Followed literally: the review reports less. Ask for everything, filter in a separate pass |
162
+ | Rules telling the model not to think or not to reason, especially naming thinking tags | Increases tag leakage — the documented effect is the opposite of the intent |
163
+ | Long stacks of prohibitions | Positive examples of the wanted style outperform instructions about what not to do |
164
+ | Aspirational rules nobody follows | Documented as "not worth including"; also teaches that rules are optional |
165
+
166
+ A flag is a candidate, not a verdict. Confirm it against the section's evidence before ruling.
167
+
168
+ Assign exactly one verdict per section:
169
+
170
+ - **keep** — evidence of a real mistake it prevents, and it belongs resident.
171
+ - **rewrite** — right content, wrong form: vague where it should be concrete ("format properly" →
172
+ "use 2-space indentation"), or contradicting another section (conflicting instructions get picked
173
+ between arbitrarily).
174
+ - **relocate** — right content, wrong layer. Stage 4 decides where.
175
+ - **delete** — no evidence, derivable, already-known, or dead wiring.
176
+ - **unjudged** — none of the three evidence kinds applies. Leave it alone and say so.
177
+
178
+ Conflicts deserve their own sweep: two sections that contradict each other are worse than either
179
+ alone, because the model may follow either one on any given session.
180
+
181
+ ## Stage 4 — RELOCATE (right content, wrong layer)
182
+
183
+ Most of what a bloated steering layer holds is not wrong — it is filed in the layer that cannot
184
+ enforce it and charges rent for trying.
185
+
186
+ | What it is | Where it belongs | Why |
187
+ |---|---|---|
188
+ | Multi-step procedure, playbook, checklist | **Skill** | Loads on demand; the descriptor is the only resident cost |
189
+ | Instruction that only matters for part of the tree | **Path-scoped rule** (`paths:` frontmatter) | Loads when matching files are touched, not every session |
190
+ | Must happen every time, no exceptions (format on save, block a path) | **Hook** | Prose is advisory; hooks are deterministic and fire regardless of what the model decides |
191
+ | Hard allow/deny boundary on tools, commands, paths | **Permission rule** | Documented as the enforcement layer for boundaries; a hook filter is best-effort and fails open on unparseable input |
192
+ | A fact the code already states, or should | **Code, test, or generated doc** | Derived facts do not drift; copied facts do |
193
+ | Dynamic per-session context (recent commits, open issues) | **SessionStart hook** | Static context belongs in the anchor; only scripted, changing context justifies a hook |
194
+ | A system Claude keeps re-reading or cannot see at all; a setup a second repo needs too | **MCP server** or **plugin** | Connect or package the capability instead of narrating it in prose that loads every session |
195
+ | A side task whose output would flood the main conversation | **Subagent** | Runs in its own context; only the result comes back |
196
+ | Nothing depends on it | **Delete** | |
197
+
198
+ Two directions that look symmetric and are not: hooks can tighten what permission rules allow but
199
+ never loosen it, and a prompt instruction is not on the enforcement list at all — it shapes what
200
+ the model attempts, so pair it with one of the two real mechanisms rather than shipping it alone.
201
+
202
+ Relocation is not free either. A hook adds a shell dependency and an administrative surface; a
203
+ skill adds a descriptor to every session. Say what the move costs, not only what it saves.
204
+
205
+ ## Stage 5 — APPLY (propose; the human decides)
206
+
207
+ Default output is a **proposal**, not an edit. Present it as one table — section, verdict,
208
+ evidence, destination — with before/after measurements from Stage 1, then stop.
209
+
210
+ Apply only what was approved, and keep the applied change checkable:
211
+
212
+ - One coherent commit, so the removal can be reverted as a unit.
213
+ - **Deletions are reversible in version control and nowhere else.** Before deleting a rule, check
214
+ whether a test, gate, or script reads that file by path — a gate that greps for a removed file
215
+ turns green by finding nothing.
216
+ - After applying, the honest verification is behavioral: the guidance's own instruction is to
217
+ *"test changes by observing whether Claude's behavior actually shifts."* Say plainly that the
218
+ effect is unverified until that observation exists. A smaller token count is not evidence that
219
+ the layer got better.
220
+
221
+ Never widen the audit into a rewrite of the project's conventions. This skill decides what loads,
222
+ not what the team believes.
223
+
224
+ ---
225
+
226
+ ## Worked example (abridged run)
227
+
228
+ **Input:** "룰이랑 훅이 밥값 하는지 좀 봐줘 — CLAUDE.md도 너무 길어진 것 같고."
229
+
230
+ **INVENTORY** — anchor 210 lines + 9 rule files, no `paths:` frontmatter on any of them, 1,100
231
+ lines resident in total across both copies of the layer; 4 hooks registered in `settings.json`;
232
+ `permissions` has `defaultMode: bypassPermissions` with zero `deny` and zero `ask` entries.
233
+
234
+ **EVIDENCE** — block log holds 6 lines over 3 weeks: 4 from the protected-file hook (all on
235
+ `.env` writes), 2 from an MCP allowlist hook, of which **1 blocked a lookup the maintainer had
236
+ explicitly asked for** — a false positive, not a save. Two of the 4 registered hooks appear zero
237
+ times; one turns out to have a matcher that cannot match any event name the CLI emits (**dead
238
+ wiring**), the other has genuinely never been triggered (**no sample** — reported as unknown, not
239
+ as safe).
240
+
241
+ **VERDICT** — 9 rules → 8 (one file restated the anchor's own principles: `delete`). Within the
242
+ survivors, section-level cuts land on: a verification-step instruction and two "double-check
243
+ before responding" clauses (generation lint), a directory-layout listing (derivable), and a
244
+ "report only blocking issues" clause in the review rule (severity suppression). Resident prose
245
+ 1,100 → 535 lines. The four rules with incident citations: `keep`, untouched.
246
+
247
+ **RELOCATE** — the release checklist (11 steps, invoked a few times per month) → skill. The
248
+ "never edit `.env`" line stays as prose *and* keeps its hook, since prose alone is not a boundary.
249
+ The dead-wired hook is deleted; the never-fired one is left in place with its status recorded as
250
+ unknown, because deleting on absence of evidence is the mistake this stage exists to avoid.
251
+
252
+ **APPLY** — proposal table presented; maintainer approves the deletions, defers the skill
253
+ extraction. Reported as: −565 resident lines, 1 dead hook removed, 1 false-positive block
254
+ identified; **behavioral effect unverified** until the next few sessions are observed.
255
+
256
+ ## Output, side effects, and stop conditions
257
+
258
+ - **Output** — the inventory with its measurement method, the evidence table (including `no
259
+ sample` entries), one verdict per section with its evidence kind, the relocation plan with
260
+ costs, and the before/after numbers.
261
+ - **Side effects** — this skill proposes; it edits only what was approved. Never touch
262
+ `permissions` or hook configuration without explicit approval: those change what the agent is
263
+ allowed to do, not merely what it reads.
264
+ - **Stop** when the project's steering layer is spread across copies you cannot tell apart, or
265
+ when the only available judgment is preference. An audit that ranks sections by taste produces a
266
+ confident list of changes that no one can defend later.
267
+
268
+ ## Cross-references (don't duplicate)
269
+
270
+ - **`recurrence-prevention`** — opposite direction: it escalates one countermeasure after a
271
+ specific defect returned. This skill audits the layer at rest; it does not decide whether a
272
+ given incident deserves a new rule.
273
+ - **`audit-service-gaps`** — audits the product against its target state; DRIFT mode covers
274
+ doc-vs-code mismatch in the product. This skill audits the agent's own steering layer.
275
+ - **`north-star`** — where a project's stated direction lives; a rule that no longer serves it is
276
+ a candidate for deletion, but the direction itself is set there, not here.
277
+ - **[references/official-criteria.md](references/official-criteria.md)** — the published quotes
278
+ behind every criterion above, with sources. Read it before ruling on a contested section.