oh-my-customcode 1.1.62 → 1.1.64

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.
Files changed (30) hide show
  1. package/dist/cli/index.js +888 -11488
  2. package/dist/index.js +52 -265
  3. package/package.json +1 -1
  4. package/templates/.claude/hooks/scripts/audit-log.sh +24 -4
  5. package/templates/.claude/hooks/scripts/failure-ledger.sh +6 -1
  6. package/templates/.claude/hooks/scripts/git-delegation-guard.sh +12 -2
  7. package/templates/.claude/hooks/scripts/model-escalation-advisor.sh +14 -3
  8. package/templates/.claude/hooks/scripts/playwright-compress.sh +13 -5
  9. package/templates/.claude/hooks/scripts/r007-r008-drift-advisor.sh +3 -0
  10. package/templates/.claude/hooks/scripts/secret-filter.sh +14 -10
  11. package/templates/.claude/hooks/scripts/stuck-detector.sh +37 -3
  12. package/templates/.claude/hooks/scripts/task-outcome-recorder.sh +99 -19
  13. package/templates/.claude/rules/MAY-optimization.md +4 -0
  14. package/templates/.claude/rules/MUST-agent-design.md +8 -0
  15. package/templates/.claude/rules/MUST-agent-teams.md +4 -0
  16. package/templates/.claude/rules/MUST-completion-verification.md +10 -0
  17. package/templates/.claude/rules/MUST-enforcement-policy.md +4 -0
  18. package/templates/.claude/rules/MUST-orchestrator-coordination.md +6 -0
  19. package/templates/.claude/rules/MUST-parallel-execution.md +2 -0
  20. package/templates/.claude/rules/MUST-permissions.md +6 -0
  21. package/templates/.claude/rules/MUST-safety.md +4 -0
  22. package/templates/.claude/rules/MUST-sync-verification.md +2 -0
  23. package/templates/.claude/rules/MUST-tool-identification.md +11 -0
  24. package/templates/.claude/rules/SHOULD-ecomode.md +4 -0
  25. package/templates/.claude/rules/SHOULD-hud-statusline.md +2 -0
  26. package/templates/.claude/rules/SHOULD-verification-ladder.md +6 -0
  27. package/templates/.claude/skills/fsd/SKILL.md +1 -0
  28. package/templates/.claude/skills/pipeline/workflows/auto-dev.yaml +5 -1
  29. package/templates/manifest.json +1 -1
  30. package/templates/workflows/auto-dev.yaml +5 -1
@@ -12,11 +12,19 @@ input=$(cat)
12
12
  # Hooks must never crash (R021); jq parse errors would otherwise abort under `set -e`. (#1650)
13
13
  printf '%s' "$input" | jq -e 'type=="object"' >/dev/null 2>&1 || exit 0
14
14
  # PostToolUse carries the tool result under `tool_response`, not `tool_output`
15
- # (measured 2026-09-03: 1764/1764 PostToolUse payloads had `tool_response`,
16
- # 0/1764 had `tool_output`), so the previous `.tool_output` read always yielded
17
- # "" and this hook never compressed anything. MCP responses arrive either as a
18
- # bare string or as `{content: [{type:"text", text:...}]}`; `.tool_output` is
19
- # kept as a fallback for events still using the older shape.
15
+ # (measured at v1.1.62: every PostToolUse record in this project's transcripts
16
+ # carried `tool_response` and none carried `tool_output`), so the previous
17
+ # `.tool_output` read always yielded "" and this hook never compressed anything.
18
+ # MCP responses arrive either as a bare string or as
19
+ # `{content: [{type:"text", text:...}]}`; `.tool_output` is kept as a fallback
20
+ # for events still using the older shape.
21
+ #
22
+ # MATCHER-SCOPED: `.stdout` and `.file.content` are read here only because this
23
+ # hook's matcher is `mcp__playwright__.*|mcp__claude-in-chrome__.*`, so those
24
+ # branches can never fire on a real Bash or Read result. This is a lossy hook —
25
+ # it REPLACES `.tool_response` with a Haiku summary — so widening the matcher to
26
+ # cover Bash/Read without first dropping those two branches would silently
27
+ # destroy genuine command output and file contents. (#1656 F)
20
28
  tool_output=$(printf '%s\n' "$input" | jq -r '
21
29
  [
22
30
  (.tool_response? | if type == "string" then . else empty end),
@@ -52,6 +52,9 @@
52
52
  # with no tool_result block) ends a turn.
53
53
  # * `thinking` blocks are interleaved with text/tool_use and never carry an R008 prefix;
54
54
  # they are filtered out before analysis.
55
+ # * NOTE (#1654, v1.1.63): narration 블록은 트랜스크립트에 type:"thinking"(signature 라벨
56
+ # narration)으로 직렬화되므로 아래 select(.type? != "thinking")이 함께 배제한다 — 의도된
57
+ # 동작: narration에는 R007/R008 마커가 실리지 않는다(실측 47블록 0건).
55
58
  #
56
59
  # ── R008 verdict: TURN-LEVEL COUNTING, not block adjacency (#1563 찐빠 #1) ─────────────
57
60
  # R008 (`.claude/rules/MUST-tool-identification.md`) says, verbatim:
@@ -12,8 +12,8 @@ command -v jq >/dev/null 2>&1 || exit 0
12
12
 
13
13
  input=$(cat)
14
14
 
15
- # Non-object stdin guard (#1650 B) — rejects ONLY non-object stdin, which carries
16
- # no scannable payload anyway; a well-formed object always reaches the scan below.
15
+ # Non-object stdin guard (#1650 B). PostToolUse stdin is always an object; a
16
+ # bare-string stdin is rejected by design, not because it is known to be empty.
17
17
  printf '%s' "$input" | jq -e 'type=="object"' >/dev/null 2>&1 || exit 0
18
18
 
19
19
  tool_name=$(printf '%s\n' "$input" | jq -r '.tool_name? // "unknown"')
@@ -21,19 +21,23 @@ tool_name=$(printf '%s\n' "$input" | jq -r '.tool_name? // "unknown"')
21
21
  # Collect every text-bearing field of the payload into one scan buffer.
22
22
  #
23
23
  # PostToolUse carries the result under `tool_response`, NOT `tool_output`
24
- # (measured 2026-09-03: 1764 PostToolUse payloads echoed into this project's
25
- # session transcripts had `tool_response` 1764/1764 and `tool_output` 0/1764;
24
+ # (measured at v1.1.62 against this project's session transcripts: every
25
+ # PostToolUse record carried `tool_response` and none carried `tool_output`;
26
26
  # the CC 2.1.259 embedded hook reference documents `"tool_response": {...}
27
27
  # // PostToolUse only`). Reading `.tool_output.output` therefore always yielded
28
28
  # "" and the scan below never ran — every AWS key, private key and PAT passed
29
29
  # through unflagged.
30
30
  #
31
- # Measured text-bearing fields, by tool:
32
- # Bash .tool_response.stdout / .stderr
33
- # Read .tool_response.file.content
34
- # Write .tool_response.content
35
- # Agent .tool_response.prompt
36
- # MCP .tool_response (string) or .tool_response.content[].text
31
+ # Text-bearing fields, by tool. This hook's matcher is `Bash|Read|Grep`, so only
32
+ # the first two rows fire in production; the rest are defensive so a matcher
33
+ # widening does not silently reintroduce the blind spot above.
34
+ # Bash .tool_response.stdout / .stderr (measured, in matcher)
35
+ # Read .tool_response.file.content (measured, in matcher)
36
+ # Write .tool_response.content (measured, out of matcher)
37
+ # Agent .tool_response.prompt / .output (measured, out of matcher)
38
+ # MCP .tool_response (string)
39
+ # or .tool_response.content[].text (defensive — unmeasured;
40
+ # MCP corpus 0 records)
37
41
  # `.tool_output.output` and a string-shaped `.tool_output` are kept as
38
42
  # fallbacks for events that still use the older shape (e.g. SubagentStop).
39
43
  #
@@ -207,6 +207,9 @@ _strip_heredoc_bodies() {
207
207
  # A literal single quote cannot be written inside the bracket expression of
208
208
  # the quote-counting expansion below, so hold one in a variable.
209
209
  local sq_char="'"
210
+ # Same expression the extraction below used to hand to "sed -n -E", now fed
211
+ # to bash's own "=~" (see the delimiter scan for why it moved).
212
+ local delim_re="^.*[^<]<<-?[[:space:]]*[\"']?([A-Za-z_][A-Za-z0-9_]*)[\"']?.*\$"
210
213
  while IFS= read -r line; do
211
214
  if [ "$in_body" -eq 1 ]; then
212
215
  # "<<-" allows leading tabs before the terminator; accept leading
@@ -253,8 +256,18 @@ _strip_heredoc_bodies() {
253
256
  printf '%s' "${out}__QUOTED_HEREDOC_OPENER__"$'\n'
254
257
  return 0
255
258
  fi
256
- delim="$(printf '%s' "$line" \
257
- | sed -n -E "s/^.*[^<]<<-?[[:space:]]*[\"']?([A-Za-z_][A-Za-z0-9_]*)[\"']?.*\$/\\1/p")"
259
+ # bash's "=~" runs the SAME POSIX ERE the "sed -n -E" here used to run —
260
+ # both engines are leftmost-longest, so the greedy "^.*" still settles on
261
+ # the LAST "<<" (verified against sed over 251 cases, #1656 E). Two forks
262
+ # per opener line are saved, but the reason it moved is correctness: sed
263
+ # aborts with "RE error: illegal byte sequence" on a line carrying invalid
264
+ # UTF-8, and under "set -o pipefail" that non-zero pipeline killed the
265
+ # whole hook. "=~" simply reports no match there.
266
+ if [[ "$line" =~ $delim_re ]]; then
267
+ delim="${BASH_REMATCH[1]}"
268
+ else
269
+ delim=""
270
+ fi
258
271
  if [ -n "$delim" ]; then
259
272
  in_body=1
260
273
  fi
@@ -292,7 +305,28 @@ is_readonly_bash_command() {
292
305
  return 0
293
306
  fi
294
307
 
295
- cmd="$(printf '%s' "$cmd" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
308
+ # sed applies "^" and "$" once PER LINE, so this trims every line of a
309
+ # multi-line command — it is NOT the whole-string parameter-expansion trim
310
+ # used in Step 6 and in _strip_heredoc_bodies, which is why it survived
311
+ # #1650 A. The loop below reproduces the per-line form exactly: each line is
312
+ # trimmed and re-terminated, then the trailing newlines are dropped the same
313
+ # way the "$( )" around the old pipeline dropped them. Two forks are saved,
314
+ # but the reason it changed (#1656 E) is correctness: sed aborts a multi-line
315
+ # command with "RE error: illegal byte sequence" at the first invalid UTF-8
316
+ # byte, and under "set -o pipefail" that non-zero pipeline killed the hook
317
+ # outright — so a heredoc body carrying raw bytes could stop the classifier
318
+ # before it ever reached a trailing "rm -rf". Parameter expansion has no
319
+ # locale dependency.
320
+ local trimmed="" tline
321
+ while IFS= read -r tline; do
322
+ tline="${tline#"${tline%%[![:space:]]*}"}"
323
+ tline="${tline%"${tline##*[![:space:]]}"}"
324
+ trimmed="${trimmed}${tline}"$'\n'
325
+ done <<< "$cmd"
326
+ while [ "${trimmed%$'\n'}" != "$trimmed" ]; do
327
+ trimmed="${trimmed%$'\n'}"
328
+ done
329
+ cmd="$trimmed"
296
330
  if [ -z "$cmd" ]; then
297
331
  echo "false"
298
332
  return 0
@@ -5,9 +5,33 @@ set -euo pipefail
5
5
  command -v jq >/dev/null 2>&1 || exit 0
6
6
 
7
7
  # Task/Agent Outcome Recorder Hook
8
- # Trigger: PostToolUse (tool == "Task" || "Agent") and SubagentStop
9
- # Purpose: Record task outcomes for model escalation decisions
8
+ # Trigger: SubagentStop (settings.json wires this script to that event and no other)
9
+ # Purpose: Record agent outcomes for model escalation decisions
10
10
  # Protocol: stdin JSON -> process -> stdout pass-through, exit 0 always
11
+ #
12
+ # MEASURED SubagentStop payload (CC 2.1.259 embedded hook schema; #1656 B):
13
+ # session_id, transcript_path, cwd, prompt_id?, permission_mode?, effort?,
14
+ # hook_event_name:"SubagentStop", stop_hook_active, agent_id,
15
+ # agent_transcript_path, agent_type,
16
+ # last_assistant_message?, background_tasks?, session_crons?
17
+ #
18
+ # Note what the event does NOT carry: no `tool_input`, no `tool_output`, no
19
+ # `model`, no `description`, no `prompt`, and no error signal of any kind. The
20
+ # selectors below therefore read the top-level fields first and fall back to
21
+ # `last_assistant_message` — the one text-bearing field SubagentStop provides —
22
+ # for the description and skill-name extraction. The `tool_input`/`tool_output`
23
+ # branches are retained as fallbacks so the script stays correct if it is ever
24
+ # re-wired to PostToolUse.
25
+ #
26
+ # This project's transcript corpus (889 files) holds ZERO SubagentStop hook
27
+ # records — SubagentStop output is not attached to the parent transcript — so
28
+ # the shape above is schema-derived, not corpus-derived.
29
+ #
30
+ # Outcome caveat: SubagentStop exposes no failure signal, so entries recorded
31
+ # from that event are always outcome=success. There is no working hand-off for
32
+ # that gap today: SubagentStop payload carries no failure signal;
33
+ # subagent-failure-advisor.sh currently reads `.tool_output.is_error`, which is
34
+ # absent here, so failure detection on this path is 0 until #1656 G is resolved.
11
35
 
12
36
  input=$(cat)
13
37
 
@@ -15,11 +39,24 @@ input=$(cat)
15
39
  # jq's parse error propagates through `set -euo pipefail` as rc=5 without this.
16
40
  printf '%s' "$input" | jq -e 'type=="object"' >/dev/null 2>&1 || exit 0
17
41
 
18
- # Extract task info — support both PostToolUse (tool_input.*) and SubagentStop (top-level) shapes.
19
- # `?` suppresses "Cannot index string with ..." when tool_input arrives as a scalar.
20
- agent_type=$(printf '%s\n' "$input" | jq -r '.tool_input.subagent_type? // .agent_type? // "unknown"')
21
- model=$(printf '%s\n' "$input" | jq -r '.tool_input.model? // .model? // "inherit"')
22
- description=$(printf '%s\n' "$input" | jq -r '.tool_input.description? // .description? // ""' | head -c 80)
42
+ # Extract agent info. Measured SubagentStop fields come first; the PostToolUse
43
+ # `tool_input.*` shape is kept as a fallback.
44
+ # `?` suppresses "Cannot index string with ..." when tool_input arrives as a scalar;
45
+ # `tostring` normalises a scalar (e.g. a numeric agent_type) instead of aborting.
46
+ agent_type=$(printf '%s\n' "$input" | jq -r \
47
+ '(.agent_type? // .tool_input?.subagent_type? // "unknown") | tostring' 2>/dev/null) \
48
+ || agent_type="unknown"
49
+ # SubagentStop carries no `model`; this resolves to "inherit" there by design.
50
+ model=$(printf '%s\n' "$input" | jq -r \
51
+ '(.model? // .tool_input?.model? // "inherit") | tostring' 2>/dev/null) || model="inherit"
52
+ # `strings` drops non-string shapes so an object-valued field degrades to "" rather
53
+ # than dumping raw JSON into the description.
54
+ description=$(printf '%s\n' "$input" | jq -r '
55
+ [ (.tool_input?.description? | strings),
56
+ (.description? | strings),
57
+ (.last_assistant_message? | strings) ]
58
+ | map(select(. != "")) | first // ""
59
+ ' 2>/dev/null | head -c 200) || description=""
23
60
 
24
61
  # Extract skill name from description or prompt
25
62
  skill_name=""
@@ -28,18 +65,35 @@ skill_name=""
28
65
  if echo "$description" | grep -qiE '(skill:|routing|→.*skill)'; then
29
66
  skill_name=$(echo "$description" | grep -oiE '[a-z]+-[a-z]+(-[a-z]+)*-?(routing|skill|practices|detection|decomposition|orchestration|pipeline|guards|cycle|plan|review|refactor|publish|version|audit|exec|analyze|bundle|report|setup|watch|lists|status|help|save|recall)' | head -1 || true)
30
67
  fi
31
- # Fallback: check prompt field for "Skill: {name}" pattern
68
+ # Fallback: check the prompt / last assistant message for a "Skill: {name}" pattern.
69
+ # SubagentStop has no `prompt`, so `last_assistant_message` is the live source here.
32
70
  if [ -z "$skill_name" ]; then
33
- prompt=$(printf '%s\n' "$input" | jq -r '.tool_input.prompt? // ""' | head -c 500)
34
- skill_name=$(echo "$prompt" | grep -oiE 'Skill:\s*[a-z]+-[a-z]+(-[a-z]+)*' | sed 's/[Ss]kill:\s*//' | head -1 || true)
71
+ prompt=$(printf '%s\n' "$input" | jq -r '
72
+ [ (.tool_input?.prompt? | strings),
73
+ (.last_assistant_message? | strings) ]
74
+ | map(select(. != "")) | first // ""
75
+ ' 2>/dev/null | head -c 500) || prompt=""
76
+ # POSIX character classes, not `\s`: BSD sed (macOS, this repo's runtime) does not
77
+ # implement the GNU `\s` escape, so `s/[Ss]kill:\s*//` stripped only the colon and
78
+ # left a leading space on every extracted skill name. Same family as the BSD `\?`
79
+ # gap recorded in R005. (#1656 B)
80
+ skill_name=$(echo "$prompt" | grep -oiE 'Skill:[[:space:]]*[a-z]+-[a-z]+(-[a-z]+)*' | sed 's/[Ss]kill:[[:space:]]*//' | head -1 || true)
35
81
  fi
36
82
 
37
- # Determine outcome
38
- is_error=$(printf '%s\n' "$input" | jq -r '.tool_output.is_error? // false')
83
+ # Determine outcome. SubagentStop carries no error signal, so this is always false
84
+ # there; `.tool_response.is_error` is the measured PostToolUse field (`.tool_output`
85
+ # is the legacy shape) and both are kept for re-wiring safety.
86
+ is_error=$(printf '%s\n' "$input" | jq -r '
87
+ (.tool_response?.is_error? // .tool_output?.is_error? // false) | tostring
88
+ ' 2>/dev/null) || is_error="false"
39
89
 
40
90
  if [ "$is_error" = "true" ]; then
41
91
  outcome="failure"
42
- error_summary=$(printf '%s\n' "$input" | jq -r '.tool_output.output? // ""' | head -c 200)
92
+ error_summary=$(printf '%s\n' "$input" | jq -r '
93
+ [ (.tool_response?.output? | strings),
94
+ (.tool_output?.output? | strings) ]
95
+ | map(select(. != "")) | first // ""
96
+ ' 2>/dev/null | head -c 200) || error_summary=""
43
97
  else
44
98
  outcome="success"
45
99
  error_summary=""
@@ -50,11 +104,29 @@ OUTCOME_FILE="/tmp/.claude-task-outcomes-${PPID}"
50
104
  TASK_COUNT_FILE="/tmp/.claude-task-count-${PPID}"
51
105
 
52
106
  # --- Pattern Detection ---
53
- # Priority: skill-specific patterns > parallel > sequential (default)
54
- pattern="sequential"
107
+ # Priority: skill-specific patterns > parallel > agent-count inference > default.
108
+ #
109
+ # INPUT SCOPE (#1656 D): the cascade below reads ONLY `tool_input.description` —
110
+ # the spawn argument the orchestrator wrote — never `$description`, which now
111
+ # falls back to `last_assistant_message`, i.e. free-form prose the subagent wrote
112
+ # about itself. A closing summary that merely says "ran the parallel review" or
113
+ # "orchestrator finished" would otherwise be recorded as
114
+ # pattern_used=parallel/orchestrator, fabricating a workflow shape out of English.
115
+ # SubagentStop carries no `tool_input`, so on that event this source is empty and
116
+ # the pattern degrades to the session-level agent-count signal (a real, non-prose
117
+ # signal) and then to "unknown" — an honest absence rather than a default
118
+ # "sequential" that reads as a measurement.
119
+ pattern_source=$(printf '%s\n' "$input" | jq -r '
120
+ (.tool_input?.description? | strings) // ""
121
+ ' 2>/dev/null | head -c 200) || pattern_source=""
122
+
123
+ if [ -n "$pattern_source" ]; then
124
+ pattern="sequential"
125
+ else
126
+ pattern="unknown"
127
+ fi
55
128
 
56
- # Check description for skill-specific workflow patterns
57
- desc_lower=$(echo "$description" | tr '[:upper:]' '[:lower:]')
129
+ desc_lower=$(printf '%s' "$pattern_source" | tr '[:upper:]' '[:lower:]')
58
130
 
59
131
  if echo "$desc_lower" | grep -qE '(evaluator.optimizer|evaluator_optimizer)'; then
60
132
  pattern="evaluator-optimizer"
@@ -87,9 +159,17 @@ if [ -f "$AGENT_START_FILE" ]; then
87
159
  fi
88
160
  fi
89
161
 
90
- # Append JSON line entry
162
+ # Append JSON line entry.
163
+ # `-c` (compact) is REQUIRED, not cosmetic: this file is consumed as JSONL by
164
+ # eval-core's outcome-parser (`content.split('\n')` + `JSON.parse(line)`) and by
165
+ # model-escalation-advisor.sh (`grep -c '"agent_type":"X".*"outcome":"failure"'`,
166
+ # `tail -N`), and the ring buffer below trims by `wc -l`. A pretty-printed entry
167
+ # spans 11 lines (measured: 9 fields plus the braces), so it broke every one of
168
+ # those readers and let `tail -50` slice
169
+ # an object in half. Sibling recorders (agent-start-recorder.sh, stuck-detector.sh)
170
+ # already use `jq -cn`. (#1656 B)
91
171
  timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ)
92
- entry=$(jq -n \
172
+ entry=$(jq -cn \
93
173
  --arg ts "$timestamp" \
94
174
  --arg agent "$agent_type" \
95
175
  --arg model "$model" \
@@ -36,6 +36,10 @@
36
36
 
37
37
  > **v2.1.234+**: macOS/Linux 네이티브 빌드의 내장 `grep`이 pathological pattern에서 메모리 고갈 대신 fail fast하고, `-m N`과 `-A/-C` 옵션을 함께 쓸 때의 context 출력 정확도가 수정되었습니다(v2.1.235에서 추가 보강). 위 「도구 이름 ≠ 그 프로그램」(#1590) 조항과 인접한 함정입니다 — 이 저장소의 Bash 도구 `grep`은 셸 함수로 셰이딩돼 있으므로, 내장 `grep` 자체의 견고성 개선과 셰이딩 문제는 **별개 축**입니다. Darwin(이 저장소 실행 환경) 네이티브 빌드에 해당합니다.
38
38
 
39
+ > **v2.1.260/265+**: (260) 여러 세션이 같은 프로젝트 디렉토리를 공유할 때 간헐적으로 발생하던 "task output swap refused" 오류가 수정되었습니다 — 위 v2.1.252 Mac tasks-dir 결함과 **같은 오류 문구의 별개 원인**이므로, v2.1.252~259 환경에서는 이 메시지가 명령 자체의 결함이 아니라 동시 세션 경합에서 나왔을 수 있습니다(R020 Read-Before-Characterize). (260) 서브에이전트가 시작한 background 명령의 1시간 제한이 제거되어, 이제 메인 세션과 동일하게 종료되거나 중지될 때까지 실행됩니다 — 서브에이전트의 장시간 background 빌드가 더 이상 60분에 무음 종료되지 않습니다. (265) 디스크에 저장되는 도구 결과에 1GB 상한이 추가되고 저장 파일이 잘렸을 때 대화 내 미리보기에 그 사실이 표시됩니다 — 저장된 도구 결과 파일을 읽을 때는 이 절단 안내 유무를 먼저 확인한 뒤 완전한 것으로 간주합니다. (265) 비대화형 세션(`-p` + stream-json 입력, Agent SDK, cloud)이 매 사용자 메시지마다 셸 작업 디렉토리를 리셋하던 결함이 수정되어 `cd`가 턴 간 유지됩니다 — 턴마다 `cd`를 재실행하는 우회책을 쓴 `-p` 스크립트는 v2.1.265+에서 그 재실행이 불필요해집니다(무해하지만 제거 가능).
40
+
41
+ > **v2.1.259/261+**: (259) `claude plugin validate --json`이 기계 판독 가능한 검증 리포트를 제공합니다(cross-ref R017 v2.1.233 `plugin validate` 노트 — 사람 판독용 출력 파싱보다 이 쪽을 우선). (261) `/context`의 토큰 계산이 토큰-계산 API를 쓸 수 없을 때 추가 소형 모델 요청 대신 **로컬 추정치**를 사용하도록 바뀌었습니다 — 엔드포인트가 다운된 동안에는 `/context` 수치가 API 실측이 아니라 추정치일 수 있습니다. (261) `claude -p --resume <file>`이 트랜스크립트에 기록된 손상된 세션 ID를 그대로 채택하던 결함이 수정되어 이제 새 세션 ID로 재개합니다 — 트랜스크립트 기반 계수(R020)에서 v2.1.261+의 재개된 `-p` 세션은 재개 대상 파일과 다른 세션 ID를 가질 수 있습니다.
42
+
39
43
  <!--
40
44
  > **v2.1.206+**: `/doctor`에 checked-in CLAUDE.md에서 코드베이스로부터 파생 가능한 내용을 잘라내도록 제안하는 체크가 추가되었습니다 — R005 "Context Optimization via HTML Comments"의 컨텍스트 절감 원칙과 정합(모델 불필요 메타데이터 축소).
41
45
  -->
@@ -79,10 +79,14 @@ Skill/rule text instructing "spawn with `model: opus`" refers to this tier — a
79
79
 
80
80
  > **v2.1.257+**: `CLAUDE_CODE_SUBAGENT_MODEL_FORCE`가 신설되어, 설정 시 `CLAUDE_CODE_SUBAGENT_MODEL`(또는 메인 모델)을 **모든** 서브에이전트에 강제 적용하며 per-spawn(Tier 3)과 agent-definition(Tier 1/2) model override를 무시합니다. 즉 위 v2.1.251 노트의 "이 env var가 project의 model pin을 더 이상 깨뜨릴 수 없다"는 서술은 **FORCE 미설정 시에 한해** 참으로 좁혀집니다. FORCE가 설정된 환경에서는 frontmatter의 model이 실행 모델의 증거가 아니므로(R020 "attempt ≠ outcome"의 모델 각도, v2.1.223 강등 경고 노트와 같은 계열) 무인 실행 전 `env | grep -c CLAUDE_CODE_SUBAGENT_MODEL_FORCE`처럼 값 노출 없이 **설정 여부만** 확인합니다.
81
81
 
82
+ > **v2.1.259+**: 프론트매터 `model:` 관련 결함 2건이 수정되었습니다 — (a) 커스텀 커맨드와 **스킬**의 프론트매터 `model:`이 interactive 세션에서 **무시**되던 결함이 수정되어, 259 이전에는 이 파일 「Skill Frontmatter」의 선택적 `model` 필드가 interactive 실행에서 **효과가 없었고**, 스킬의 실제 실행 모델은 스킬이 무엇을 선언했든 세션 모델 그대로였습니다(R020 "attempt ≠ outcome"의 스킬 model pin 각도). (b) auto mode가 커맨드·스킬 프론트매터 `model:`이 명명한, 지원하지 않는 모델로 turn을 실행하던 결함이 수정되어 이제 세션 모델을 유지합니다 — 즉 auto mode에서는 스킬 `model:` pin이 세션 모델에 조용히 override될 수 있으므로, 실제 실행 모델은 프론트매터가 아니라 v2.1.223 강등 경고로 확인합니다.
83
+
82
84
  > **Claude Fable 5 (access via CC v2.1.170+)**: Mythos-class model, GA on the Claude API and positioned as a tier above Opus — its capabilities exceed any previously GA model. CC v2.1.170 is the client version that adds access (the model's GA is an API/platform property, not a CC-release milestone). Available via frontmatter full ID `claude-fable-5` (Tier 2) or Agent tool `model: fable` (Tier 3) — NOT via a Tier-1 frontmatter alias. Reserve for the most complex reasoning where its capability premium is warranted; `sonnet` remains the default for general tasks and `opus` for architecture (cost/latency awareness, R005). CC v2.1.170 also fixes session transcripts not saving (and not appearing in `--resume`) when launched from a VS Code integrated terminal or any shell inheriting Claude Code env vars — relevant to transcript-dependent skills (`homework`, `episodic-memory`). Closes #1352.
83
85
 
84
86
  > **v2.1.257+**: Claude Fable 5.1(`claude-fable-5-1`)이 추가되어 **기본 Fable 모델**이 되었습니다 — 1M context, $10/$50 per Mtok(캐시 읽기 $0.25/Mtok). Tier-3 `model: fable` alias는 이제 Fable 5.1로 해석됩니다(단 Claude apps gateway 세션은 게이트웨이가 아직 Fable 5.1을 미지원해 당분간 Fable 5로 유지됩니다 — `/model`에서 명시 선택해야 Fable 5.1 사용 가능). frontmatter에서 확정하려면 Tier-2 full ID `claude-fable-5-1`을 쓰고, 기존 `claude-fable-5` pin은 그대로 Fable 5에 남습니다 — Tier-1 alias 해석 주체는 CC라는 위 원칙(v2.1.219/222 노트와 동일 계열)의 재확인입니다.
85
87
 
88
+ > **v2.1.260+**: Fable 5.1 관련 결함 3건이 수정되고 개선 1건이 적용되었습니다 — `model: fable` 에이전트가 `ANTHROPIC_DEFAULT_FABLE_MODEL` pin에 `[1m]` 태그를 붙여도 이를 무시하고 200K 컨텍스트 창으로 조용히 실행되던 결함(즉 260 이전에는 Fable에 붙인 Tier-2 `[1m]` 접미사가 이 env pin 경로에서 **존중되지 않았습니다**); `/model` 피커가 Fable 5.1을 표시하지 않던 결함(`/model claude-fable-5-1` 직접 입력만 동작); Fable 5.1의 prompt caching이 도구 결과 이후 첨부된 컨텍스트를 커버하지 못해 매 도구-호출 turn마다 uncached 입력으로 재전송되던 결함; 그리고 세션 중 `/effort` 변경이 이제 Fable 5.1의 prompt cache를 무효화하지 않도록 개선되었습니다. 또한 (260) 1M-컨텍스트 모델의 auto-compact가 강화되어 Opus·Fable 세션이 1M-token 한도 직전에 compact하며, 초대형 컨텍스트의 복구 compaction이 10분 타임아웃으로 끊기지 않습니다 — 위 R013 v2.1.251 Sonnet 5 1M auto-compact 노트를 Opus/Fable로 확장합니다(cross-ref R013).
89
+
86
90
  <!-- ARCHIVED CC version notes (historical):
87
91
  > **v2.1.173+**: Fable 5 model IDs carrying a `[1m]` suffix are now auto-normalized (the suffix is stripped) because Fable 5 includes 1M context by default. Use `claude-fable-5` / `model: fable` WITHOUT a `[1m]` suffix — appending it is redundant and normalized away. (The `[1m]` suffix remains meaningful for Opus/Sonnet IDs.)
88
92
 
@@ -93,6 +97,8 @@ Skill/rule text instructing "spawn with `model: opus`" refers to this tier — a
93
97
 
94
98
  > **Fable 5 Effort 전략**: Fable 5는 **high effort가 기본값**이며, `xhigh`는 capability-sensitive 작업(최고난도 아키텍처/추론)에 한정해야 합니다. Fable 5의 `low`/`medium` effort조차 이전 세대 모델의 `xhigh`를 상회하는 품질을 보이므로, Fable 5를 사용하는 실행 에이전트는 `effort` 필드를 신중히 명시하고 불필요한 `xhigh` 남용을 지양합니다(R005 비용/지연 인식과 정합).
95
99
 
100
+ > **v2.1.267+**: `effort:` 프론트매터 관련 결함·신규 상한 3건 — (a) 커스텀 커맨드·스킬·서브에이전트의 `effort:` 프론트매터가, 기본 effort가 여전히 고정된 모델(Opus 4.7, Opus 4.8, Fable 5)에서 **무시**되던 결함이 수정되었습니다. 즉 267 이전에는 이 파일의 "스킬 `effort`가 에이전트 `effort`보다 우선한다"는 서술과 위 「Fable 5 Effort 전략」의 `effort` 명시 지침이 Fable 5 / Opus 4.8 에이전트에서 **런타임 효과가 없었습니다** — 프론트매터 effort는 실제 실행된 effort의 증거가 아니었습니다. (b) 신규 `maxEffortLevel` 설정(최상위 또는 `modelSettings` 하위 모델별)이 모든 provider에서 effort 레벨 상한을 강제합니다 — 사용자는 여전히 더 낮은 레벨을 선택할 수 있습니다. 이는 프론트매터 `effort`보다 **상위에 위치하는 설정 레벨 상한**이므로, `xhigh`를 선언한 에이전트도 상한이 설정돼 있으면 그 상한에서 실행됩니다(프론트매터로 유추하지 말고 실효 effort를 확인). (c) `/model opusplan[1m]`이 "Model not found"로 거부되던 결함이 265에서 수정되어, `/model` 명령에서 이 표기가 이제 수용됩니다 — 프론트매터 `model: opusplan[1m]` 경로는 릴리즈 노트가 언급하지 않으므로 미실측입니다.
101
+
96
102
  > **Mythos 5 (`claude-mythos-5`)**: Project Glasswing 한정 공급 모델로, **GA가 아닙니다** — Fable 5(GA, 위 "Model Specification — 3 Tiers"의 `claude-fable-5`/`fable`)와 구분해야 합니다. 특성: adaptive-thinking 전용 아키텍처 + 안전 분류기가 개입 시 `stop_reason: "refusal"`로 fallback하는 체계를 가집니다. oh-my-customcode 에이전트 frontmatter에는 아직 alias를 등록하지 않습니다(비-GA, 공급 제한).
97
103
 
98
104
  > **프롬프팅 패턴 상호참조**: Fable 5/Mythos 5 대상 프롬프팅 패턴(effort 조합, adaptive-thinking 활용, refusal fallback 대응)의 상세 가이드는 `guides/claude-code/16-fable5-prompting.md`를 참조하세요.
@@ -139,6 +145,8 @@ This is a settings-level resilience mechanism, distinct from the per-agent `mode
139
145
 
140
146
  Key optional fields: `memory`, `effort`, `skills`, `soul`, `isolation`, `background`, `maxTurns`, `maxTokens`, `mcpServers`, `hooks`, `permissionMode`, `disallowedTools`, `limitations`, `domain`, `disableSkillShellExecution`, `experimental.cacheTtl` (v2.1.248+). Supported since CC v2.1.63+. See full optional frontmatter via Read tool.
141
147
 
148
+ > **v2.1.261/265/267+**: 서브에이전트/스킬 런타임 관련 3건 — (261) `--append-subagent-system-prompt-file`이 신설되어 커맨드라인에 담기 힘들 만큼 큰 서브에이전트 시스템 프롬프트를 파일에서 읽습니다(R009의 ~5000-token 프롬프트 휴리스틱과 정합 — 대형 프롬프트는 단순 파일 로드가 아니라 우선 분할의 신호입니다). (265) forked 스킬(`context: fork`)이 착수(kickoff) 프롬프트를 스트리밍하지 않고, `--forward-subagent-text`와 함께 쓸 때 그 텍스트 turn을 stream-json progress 이벤트로 내보내지 않던 결함이 수정되었습니다 — 아래 「Context Fork Criteria」의 10/12 `context: fork` 스킬을 `-p --output-format stream-json`으로 실행할 때 관련되며, 구버전에서는 fork progress 이벤트 부재가 fork가 실행되지 않았다는 증거가 아니었습니다. (267) `--system-prompt`/`--append-system-prompt`로 시작한 서브에이전트·세션이 이제 시스템 프롬프트와 도구 정의를 매 요청마다 재렌더링하는 대신 한 번만 기록합니다(prompt-cache 안정성) — `--system-prompt-snapshot off`는 프롬프트 반복 작업을 위해 매 요청 새로 렌더링합니다.
149
+
142
150
  ### Note on `skills:` field
143
151
 
144
152
  The `skills:` frontmatter field is **advisory metadata** consumed by oh-my-customcode tooling (graph-builder, mgr-sauron) for documentation and validation. It is **NOT a runtime allowlist** — Claude Code does not filter the available skills based on this field, and subagents can invoke any registered skill regardless of what `skills:` declares. Use it to document a subagent's intended skill dependencies; do not rely on it for access control.
@@ -432,6 +432,10 @@ Cross-reference: R020 ("actual outcome ≠ attempt" — verifying that a command
432
432
 
433
433
  > **v2.1.257+**: 세 건이 함께 수정되었습니다. (a) leader의 mailbox 쓰기가 잠시 잠긴 사이 teammate permission request가 **두 번 응답**되던 결함 수정 — v2.1.224/251 SendMessage·inbox 신뢰성 계열의 연장이며, 구버전에서 승인이 2회 적용된 흔적은 이중 승인 의도의 증거가 아닙니다. (b) tmux/iTerm2 pane의 teammate가 shutdown 확인 후에도 열려 있던 결함 수정 — 위 Lifecycle의 `TeamDelete` 이후 pane 잔존은 더 이상 정상이 아닙니다. (c) `/fork`가 원 대화의 prompt cache를 새 background 세션에서 유지하도록 개선(worktree briefing이 system-prompt 변경 대신 메시지로 도착) — R009 fork 컨텍스트 상속 노트의 비용 각도. 이 저장소는 `TeamCreate` 미등록으로 R018이 dormant이므로 (a)(b)는 기록용, (c)는 fork 사용 시 즉시 해당합니다.
434
434
 
435
+ > **v2.1.260+**: `SendMessage`/teams 신뢰성 계열에서 세 건이 추가로 수정되었습니다 — 다른 에이전트를 `SendMessage`로 resume한 서브에이전트가 그 에이전트의 완료 알림을 받지 못하고(알림이 대신 main conversation으로 갔습니다), in-process teammate의 트랜스크립트가 긴 API 재시도 대기(`CLAUDE_CODE_RETRY_WATCHDOG` 등) 중 재시도 알림에 실제 메시지가 덮여 유실되거나 비어 보였으며, background로 이동한 세션이 `ListAgents`에 동일 이름의 "interactive" 유령 쌍둥이로 **두 번** 나타나 뷰어 쪽이 `SendMessage` 전달을 받는 문제가 있었습니다 — 위 v2.1.229 `ListAgents` 노트("열거됐다고 도달 가능하다고 가정하지 않는다")를 확장합니다: 나열된 이름이 단일 실제 대상이라는 보장조차 없었습니다. 결론은 그대로입니다 — 결정론적 ground-truth만이 완료 증거입니다.
436
+
437
+ > **v2.1.261+**: 다른 머신의 OFFLINE Remote Control 세션으로 `SendMessage`를 보내면 전송됨으로 읽혔으나, 이제 그 머신이 재연결될 때까지 전달이 큐잉된다는 결과를 반환합니다 — v2.1.224 inbox 쓰기 실패·v2.1.251 최종 답변 유실에 이은 이 계열의 세 번째 사례이며, 구버전의 "Message sent"는 상대가 온라인이라는 보장조차 아니었습니다. 같은 릴리즈에서 in-process agent-team teammate가 두 번째 턴에 첫 턴의 도구·스킬 announcement를 재전송해 요청 prefix가 바뀌고 prompt cache를 놓치던 결함도 수정되었고, (265) teammate·resumed 서브에이전트가 `SubagentStart` 훅 컨텍스트와 preloaded skill을 prompt prefix 밖으로 이동시키던 결함도 수정되었습니다. 이 저장소의 R018은 `TeamCreate` 부재로 dormant이므로, 이 노트는 cross-session `SendMessage` 경로에 한해 기록합니다.
438
+
435
439
  <!-- ARCHIVED CC version note (historical):
436
440
  > **CC v2.1.162+**: `claude agents --json` now includes a `waitingFor` field showing what a waiting session is blocked on (e.g. a permission prompt). Use it as an additional deterministic ground-truth signal — a member with a non-empty `waitingFor` is blocked on input (needs unblocking), NOT silently stalled (reassign per stall handling below). This distinguishes the two failure modes the verification is meant to separate.
437
441
 
@@ -388,6 +388,12 @@ This applies when a change touches a field that participates in an override/prec
388
388
  |--------------|----------|
389
389
  | Plan a provider/endpoint switch as N commands without reading the config's override chain | Read the full config schema (which field wins, defaults, inheritance) → enumerate EVERY field the switch touches (incl. base_url) → then plan |
390
390
 
391
+ **훅 스크립트 각도 — stdin 필드 형상은 실측 후 편집 (Origin: #1658 #1, v1.1.62)**: 훅 스크립트가 읽는 stdin 필드(`tool_input`/`tool_response`/`agent_id` 등)를 편집·가드·억제하기 전에 **실제 페이로드 형상을 실측**한다 — 트랜스크립트의 `attachment.type=="hook_success"` 레코드에서 `attachment.stdout`이 pass-through 훅이 되돌린 stdin 원문이며, CC 바이너리 내장 훅 문서로 교차검증한다. 처방("가드 추가·`?` 억제")만 위임하면 선택자 결함 위에 가드를 얹어 마지막 실패 신호까지 지운다. 실증: v1.1.62에서 `secret-filter.sh`가 PostToolUse에 존재하지 않는 `tool_output`(0/1764)을 읽어 실제 페이로드를 한 번도 스캔하지 않던 선재 결함 위에 `?` 억제가 추가됐고(rc=5 신호 소멸), 적대적 리뷰가 실측 형상 재현으로 FAIL 판정해 `tool_response`(1764/1764)로 교체했다. 훅 편집 위임서 표준 문안: "스크립트가 읽는 stdin 필드는 `hook_success` 레코드로 형상 실측 후 편집".
392
+
393
+ | Anti-pattern | Required |
394
+ |--------------|----------|
395
+ | 훅 위임서에 "가드 추가·`?` 억제" 처방만 전달 | 읽는 필드의 실제 형상(`hook_success` stdin 원문 + 바이너리 훅 문서) 실측을 위임서 완료 조건에 포함 |
396
+
391
397
  Sibling discipline to Read-Before-Characterize (that rule governs diagnosis — don't label before reading; this one governs edit-planning completeness — enumerate every interdependent field before editing). Cross-ref: R023 (verification ladder — config completeness is a Tier-1 deterministic pre-check).
392
398
 
393
399
  ### Degraded-Output Re-Verification Gate (529 / buffering)
@@ -420,6 +426,10 @@ Origin: #1269 ① (R020 self-violation, session 106).
420
426
 
421
427
  **교훈**: 위 Core Rule("actual outcome ≠ attempt")은 방향이 없다 — 도구가 성공을 보고하든 실패를 보고하든, 보고 자체는 ground-truth가 아니다. 실패 보고를 받았다고 곧바로 재시도·롤백에 들어가지 말고, 먼저 실제 산출물 상태를 확인한다.
422
428
 
429
+ > **v2.1.259/261+**: "정지 보고 ≠ 실제 정지" 결함 3건이 수정되었습니다. (259) Stop이 remote-control 세션의 background agent·workflow를 실제로 멈추지 못했던 결함 — 이제 kill된 task는 프로세스가 종료될 때까지 계속 표시되고 재정지 가능합니다. (261) SDK·cloud 세션이 첫 프롬프트 직후, turn이 시작하기 전에 도착한 Stop/interrupt를 무시하고 turn을 끝까지 실행하던 결함이 수정되었습니다. (261) 터미널 진행 표시(iTerm2, Ghostty, ConEmu)가 background workflow/agent가 아직 실행 중인데도 세션을 완료된 것으로 표시하던 결함이 수정되었습니다. 세 건 모두 이 섹션 원칙의 **역방향 쌍둥이**입니다 — "정지됨"/"완료됨" 신호도 ground-truth가 아니므로, 인터럽트가 실제로 적용됐다고 단정하기 전에 프로세스/run 상태(`gh run list`, task 패널, `pgrep`)로 확인합니다. 같은 릴리즈에서 (259) 이전 정지 실행이 종료 중인 상태에서 workflow run을 재개하면 그 에이전트들이 **중복 실행**될 수 있던 결함도 수정되었습니다(cross-ref R023 Workflow resume).
430
+
431
+ > **v2.1.265/267+**: (265) 이전 프로세스가 도구 실행 중 죽은 뒤 재개하면 마지막 프롬프트를 더 이상 재작성하지 않고, 중단된 도구 호출을 유지하며 **interrupted로 명시 표시**합니다 — 위 v2.1.246 행(인터럽트된 셸 명령이 단순 "실행됨"으로만 표시)의 연장선으로, 이 경로에서는 이제 트랜스크립트에 명시적 interrupted 마커가 남으므로 v2.1.265+에서는 그 부재가 유의미한 증거지만 구버전에서는 아닙니다. (267) `/compact` 또는 다른 슬래시 커맨드 직후 `-p --resume`으로 재개할 때 가짜 "Continue from where you left off." turn이 더 이상 삽입되지 않습니다 — `/fsd` 류 `-p` 루프에서 이런 turn을 사용자 입력으로 오인할 수 있었던 경로와 관련됩니다(R015: 이것은 지시가 아닙니다). 또한 큰 세션(트랜스크립트 5MB 초과)을 재개할 때 병렬 도구 호출과 그 훅 출력이 재로드된 대화에서 누락되던 결함도 수정되었습니다(cross-ref R021) — 회고적 트랜스크립트 계수(「Self-Violation Counting Is Also Diagnosis」)에서, 구버전으로 재개된 5MB 초과 세션은 도구 호출이 유실됐을 수 있으므로 그런 트랜스크립트의 계수는 **하한값**으로 취급합니다.
432
+
423
433
  ### CI Publish-Step Error vs Published-Artifact Ground Truth
424
434
 
425
435
  > Origin: #1332 — `npm publish --provenance` emitted a Sigstore `TLOG_CREATE_ENTRY_ERROR` 409, but the publish step's `|| npm view <pkg>@<ver>` fallback recovered (the package WAS published) and release.yml succeeded on all jobs. A subagent read the tlog error in the logs and prematurely declared the run "failed", recommending a re-run; deterministic ground-truth (`npm view`, `gh release view`) showed the release had fully succeeded.
@@ -47,6 +47,10 @@ oh-my-customcode uses an **advisory-first enforcement model**. Most rules are en
47
47
 
48
48
  > **v2.1.248+**: 훅 관측성이 두 건 강화되었습니다 — (a) `PermissionRequest`/`PreToolUse` 훅이 유효하지 않은 응답을 출력해 background session이 조용히 대기하던 결함이 수정되어, 이제 `claude agents` 행이 해당 훅 이름과 스키마 에러를 표시합니다. (b) 훅이 stdout으로 낸 `{…}` 객체가 유효한 JSON이 아닐 때 조용히 plain text로 처리하던 결함이 수정되어, 이제 parse 에러와 함께 훅 에러로 보고됩니다. 위 v2.1.214 "훅 stdout JSON이 스키마 검증에 실패할 때 exit code 2가 문서대로 차단하지 못하던 문제" 노트와 같은 계열 — 훅 실패가 무음에서 가시화되는 흐름의 연속입니다.
49
49
 
50
+ > **v2.1.259+**: blocking Stop 훅이 발동한 턴의 **다음** 턴이 그 턴의 모델 reasoning을 잃고, 일부 모델에서는 prompt cache까지 miss하던 결함이 수정되었습니다. 이 저장소의 Stop-hook 계층(`session-reflection.sh`, retroactive R007/R008 advisory)에 직접 해당됩니다 — 구버전에서는 Stop 훅의 block이 차단 자체 외에 **숨은 비용**(reasoning 유실 + cache miss)을 수반했으므로, v1.1.53~56에 기록된 "Stop 훅 잠식" 패턴의 **원인 후보 중 하나**로 재해석할 여지가 있습니다. 단 확정 원인은 아니며 가설 후보로만 취급합니다(R020 hypothesis 규율). 같은 릴리즈에서 `claude plugin validate --json`이 기계 판독 가능한 리포트를 제공하게 되었습니다(위 R017 v2.1.233 `plugin validate` 노트의 연장) — Tier-1 결정론적 검사로 활용 가능합니다.
51
+
52
+ > **v2.1.261/265/267+**: resume 시 훅 컨텍스트 무결성 결함 3건이 수정되었습니다. (261) 세션 재개 시 **병렬 도구 호출 주변**의 훅 출력 등 컨텍스트가 유실되어 재개된 요청이 달라지던 결함이 수정되었습니다. (267) 대용량 세션(트랜스크립트 5MB 초과) 재개 시 병렬 도구 호출과 그 훅 출력이 누락되던 결함이 수정되었습니다. (265) agent teammate와 재개된 서브에이전트가 이후 턴에서 `SubagentStart` 훅 컨텍스트와 preload된 스킬을 prompt prefix 밖으로 이동시켜 prompt-cache 재사용을 깨뜨리던 결함이 수정되었습니다. 함의: 구버전에서 재개된 세션의 대화에 어떤 훅의 출력이 보이지 않는다고 해서 그 훅이 **발화하지 않았다**는 증거는 아닙니다 — 「배선 확인 ≠ 전달 확인 ≠ 발화 확인 ≠ 로드 확인」4층 구분에 5번째 각도(**발화 ≠ 재개 후 보존**)가 추가됩니다. 또한 (267) managed `allowedHttpHookUrls`/`httpHookAllowedEnvVars`가 읽을 수 없을 때 아무것도 허용하지 않도록(fail-closed) 변경되었습니다.
53
+
50
54
  ## Why Advisory-First
51
55
 
52
56
  1. **Agent flexibility**: Hard blocks can trap agents in unrecoverable states
@@ -390,6 +390,8 @@ Origin: #1595 #2 (v1.1.48 세션 — `git checkout -b release/v1.1.48 develop`
390
390
  | 파일 소유권만 고지하고 동일 검증 명령을 각 에이전트 완료 조건에 넣어 병렬 발주 | 검증 명령 공유를 고지하거나 검증을 오케스트레이터가 직렬 1회로 회수 |
391
391
  | 공유 `$TMPDIR`에 고정 경로로 임시 파일을 쓰고 그 디렉토리를 전수 계수 | 에이전트별 고유 경로 사용 + 그 경로만 계수 |
392
392
 
393
+ **순차 위임도 고지 대상 — 직전 완료 변경분의 출처 (Origin: #1658 #4)**: 병렬 형제뿐 아니라 **이미 워킹트리에 있는 미커밋 변경분의 출처**(직전에 완료한 에이전트와 그 담당 파일)를 위임서에 한 줄 고지한다. 고지가 없으면 후속 에이전트가 `git diff`에 보이는 타 변경분을 "형제 담당분"으로 오귀속해 서술한다(v1.1.62 세션 3건 — 행동에는 영향 없었으나 보고가 오염). 문안: "워킹트리의 미커밋 변경 중 X·Y는 직전 에이전트 [N]의 완료분이다 — 건드리지 말고 보고에서도 네 변경분과 구분하라."
394
+
393
395
  > Origin: #1518 (찐빠 #3 — 미고지 git 에이전트가 형제를 "외부 프로세스"로 오귀속; 같은 세션에서 고지한 4개 구현 에이전트는 전원 정확히 구분 보고 — 대조 실증). Cross-ref: R009 (병렬 실행 조건).
394
396
 
395
397
  > Origin 보강: #1598 — 파일이 완전 disjoint한 병렬 배치에서 위양성 4종 발생(judge.sh 테스트 7건 ENOENT: 두 테스트가 tracked `verdict-schema.json`을 cp→rm→복구 / reviewers.sh 타임아웃 테스트 간헐 실패: CPU 포화 / "임시 파일 누수 1건" 오측정: 형제 잔여물, 격리 셔임 재측정 시 0). **3종의 원인은 오케스트레이터가 위임서에 넣은 완료 조건 자체였다** — 형제 고지의 결함이 아니라 고지 항목의 누락이다.
@@ -532,6 +534,10 @@ Before spawning any agent:
532
534
 
533
535
  > **★ v2.1.257+**: 프로젝트 스코프 `.claude/settings.json`/`.claude/settings.local.json`의 `defaultMode: "bypassPermissions"`가 이제 **무시**됩니다(`"auto"`와 동일 취급) — user 또는 managed settings에 설정하거나 `--permission-mode` 플래그로 전달해야 합니다. 이 섹션은 v2.1.212+에서 "통제점은 부모 세션의 permission mode"라고 규정했는데, 그 부모 세션 mode를 프로젝트 settings로는 더 이상 켤 수 없으므로 통제점이 **user/managed settings 또는 `--permission-mode` 플래그**로 한 단계 더 밀려납니다. 이 저장소 실측(2026-09-02): `.claude/settings.json`과 `.claude/settings.local.json` 둘 다 `permissions.defaultMode = "bypassPermissions"`였으나 `~/.claude/settings.json`(user)은 `"auto"`였고, 세션 훅 컨텍스트도 "auto mode is active"를 보고했습니다 — 즉 v2.1.257 이후 "bypass로 무인 실행 중"이라는 전제가 **조용히 깨져 있었습니다**. 무인 루프(`/fsd`) 착수 전에는 user settings의 `permissions.defaultMode`를 조회하거나 `--permission-mode bypassPermissions`를 명시적으로 전달해 유효 모드를 확인합니다 — R002/R006의 이 섹션 canonical 참조는 그대로 유지합니다. 같은 릴리즈에서 agent view(`←`)로 dispatch된 세션이 원본 세션의 permission mode를 강제 상속하던 결함도 수정되어, 대상 디렉토리의 `defaultMode`와 agent의 `permissionMode`가 이제 존중됩니다.
534
536
 
537
+ > **v2.1.259+**: 무인 헤드리스 호스트용 신규 실행 플래그 `--permission-prompts none`이 추가되었습니다 — 프롬프트를 발생시켰을 대상은 자동으로 **거부**되고, 나머지는 활성 permission mode(auto mode 포함)가 그대로 판정합니다. 이는 위 v2.1.257 노트가 규정한 user-scope `permissions.defaultMode`, `--permission-mode` 플래그에 이은 **세 번째 통제점**입니다 — 이 플래그는 bypass를 부여하지 않고 "프롬프트했을 상황"을 "거부"로 전환할 뿐이므로, 이 플래그로 실행한 `/fsd` 류 루프는 프롬프트에 걸려 정지하지 않는 대신 프롬프트가 필요했을 스텝이 **거부(실패)로 종료**될 수 있습니다. 무인 스텝이 멈춘 것과 거부된 것은 R020 진단 축이 다릅니다 — 이 플래그로 실행한 경우 정지한 프롬프트가 아니라 **거부 에러 메시지**를 먼저 찾습니다. 또한 v2.1.260에서 **서브에이전트**가 시작한 백그라운드 명령의 1시간 시간 제한이 제거되어(메인 세션과 동일하게 종료·중지 시까지 실행), v2.1.232 background-spawn 노트와 결합하면 서브에이전트의 장기 백그라운드 명령이 더 이상 60분에 죽지 않으므로, 260+에서는 "1시간 뒤 조용히 종료됨"을 더 이상 유효한 진단으로 쓰지 않습니다.
538
+
539
+ > **v2.1.259/265+**: (259) 중첩(nested) 백그라운드 서브에이전트의 결과가 이제 **부모 서브에이전트의 트랜스크립트**에 저장되어, resume된 서브에이전트가 결과를 유지하고 공유 트랜스크립트에서 전달이 드러납니다 — 이는 CC의 기본 nested-spawn 능력을 서술할 뿐이며, 위 flat-delegation **정책**(Core Rule)은 그대로 불변입니다. (259) remote-control 세션에서 `Stop`이 백그라운드 에이전트·워크플로우를 실제로 멈추지 못하던 결함이 수정되어, kill된 작업이 프로세스가 실제로 종료될 때까지 계속 보이고 재중지 가능합니다(cross-ref R020 역방향 노트 — "실패/중단 보고 ≠ 실제 실패"). (265) foreground로 스폰한 서브에이전트를 resume하면 도구 목록과 시스템 프롬프트 prefix가 바뀌어(prompt-cache 파손) 있던 결함이 수정되었고, non-interactive 세션(`-p` stream-json / SDK / cloud)이 사용자 메시지마다 셸 cwd를 초기화하던 결함도 수정되어 이제 `cd`가 턴 사이에 유지됩니다 — 턴별로 명령을 연쇄하는 `-p` 위임 스크립트에 직접 영향을 줍니다.
540
+
535
541
  > **cross-ref (v1.1.50 실측)**: R018의 `maxTurns` partial 표시(v2.1.246)가 R020 「Verification-Delegation Non-Termination」 mid-step 종료 패턴의 **실재 원인 중 하나로 확정**되었다 — 위임 프롬프트에 종료 금지 clause를 아무리 강화해도, 절단 주체가 플랫폼 turn 한도이면 에이전트에 닿지 않는다. 위임 경계를 단일 목표로 분할하는 것(R020 해당 조항)이 여전히 1차 방어선인 이유다. 상세는 R018 (MUST-agent-teams.md) Member Completion Verification 섹션.
536
542
 
537
543
  ## Agent Capability Pre-Check
@@ -133,6 +133,8 @@ Reference: #1320 (fix), #1321 (session 113 retrospective 찐빠 #1), `feedback_l
133
133
 
134
134
  > **v2.1.229+**: workflow fan-out이 같은 prefix를 공유하는 sibling agent를 **stagger**해 후속 에이전트가 prompt prefix 캐시를 재사용합니다(`CLAUDE_CODE_WORKFLOW_PREFIX_STAGGER_MS=0`으로 비활성). 즉 동일 접두사 병렬 배치는 동시 발사가 아니라 **의도적 시차 실행**이며, 스폰 직후 일부 에이전트의 시작 지연을 아래 Adaptive Parallel Splitting의 stall 신호로 오판하지 않습니다 — 구버전에서는 각 형제가 접두사 비용을 중복 지불했습니다. 또한 CPU 제한 컨테이너에서 dynamic workflow가 호스트 코어 수를 쓰던 문제가 수정되어, 컨테이너 실행 시 실효 동시성이 위 표의 cap보다 낮을 수 있습니다.
135
135
 
136
+ > **v2.1.265/267+**: 병렬/백그라운드 배치의 비용 모델에 영향을 주는 prompt-cache 안정성 수정 다수입니다. (265) 전경(foreground)으로 스폰된 subagent를 재개(resume)하면 그 에이전트의 tool 목록과 system prompt prefix가 바뀌어 prompt-cache 재사용이 깨지던 결함이 수정되었습니다. (267) 대화에서 fork된 background worker가 세션 도중 대화의 tool 블록에 `EnterWorktree`를 추가하던 결함(cache 단절), `ToolSearch`가 없는 세션에서 세션 도중 추가된 MCP/plugin tool이 tool 목록에 즉시 실려 cache가 단절되던 결함(지원 모델은 이제 deferred definition으로 전달받음), `/model`로 모델을 전환하면 모든 tool 정의를 다시 전송해 cache miss가 발생하던 결함이 수정되었습니다. 이 규칙의 비용 모델에 대한 함의: 구버전에서는 fork/resume이 prefix를 바꿀 때마다 위 v2.1.229 prefix-stagger 이점이 조용히 소실됐으므로, 그 시기 버전에서 측정된 "병렬 신규 스폰"과 "long-lived 재사용"(아래 Fable 5 노트) 간 비용 차이는 현행 동작과 비교 불가합니다. 또한 (261) background agent가 재개(resume)되지 못하고 wake-up이 tight loop로 재시도되며 CPU가 지속 고점유되던 결함이 수정되었습니다 — 구버전에서는 병렬 배치 중 CPU 포화가 배치 자체가 아니라 재개 불가한 background agent에서 비롯될 수 있었습니다(「파일 disjoint ≠ 자원 disjoint」 CPU 행과 교차 참조).
137
+
136
138
  > **Fable 5 long-lived subagent reuse (Origin: #1435)**: Fable 5는 long-lived subagent 재사용(단일 subagent가 여러 단계를 이어서 수행)에 강함 — 현행 R009 병렬 실행 원칙과 상충하지 않으며, Fable 5 실행 시 short-lived 병렬 다수 대신 long-lived 재사용도 유효한 선택지. 상세는 `guides/claude-code/16-fable5-prompting.md`.
137
139
 
138
140
  ## Adaptive Parallel Splitting
@@ -104,6 +104,12 @@ Use a `"*"` deny rule in `settings.json` to enforce a deny-by-default posture, t
104
104
 
105
105
  > **v2.1.257+**: 권한검사 보강 5건이 추가로 확인되었습니다. (a) auto mode에서 `permissions.ask` 규칙이 매칭 명령이 복합 명령·서브셸 내부에서 실행될 때 건너뛰어지던 결함이 수정되어 확인 프롬프트 없이 실행되지 않습니다 — 위 「allow ≠ classifier」 계열의 ask 층 보강입니다. (b) zsh가 bash와 다르게 파싱하는 `[[ ]]` 조건문을 auto-approve하던 결함이 추가로 수정되었습니다 — v2.1.221/238에 이은 3번째 보강이며, 이 저장소의 Bash 도구 실행 셸이 zsh이므로 직접 해당합니다. (c) `permissions.blockReadsOutsideWorkingDirectories` 설정이 신설되어, auto mode에서 작업 디렉토리 밖 첫 파일 읽기 전 1회 프롬프트를 표시하고 그런 읽기를 차단하는 옵션을 제공합니다. (d) `allowManagedPermissionRulesOnly`가 활성 상태일 때 첫 settings reload 이후 `--disallowedTools`와 세션 deny 규칙이 탈락하던 결함이 수정되었습니다. (e) 워크트리 격리 세션이 git을 건드리지 않는 Bash 루프·`$VAR` 읽기·`"$(…)"`·heredoc을 "too complex to verify that it stays inside the worktree"로 거부하던 결함이 수정되었습니다 — R009 워크트리 병렬 위임 시 이런 복합 명령의 거부를 더 이상 격리 결함으로 진단하지 않습니다.
106
106
 
107
+ > **v2.1.259 → 260 (도입 후 롤백)**: v2.1.259는 Bash `Read()` deny 규칙의 적용 범위를 옵션 값으로 주어진 파일(`--ignore-revs-file=.env`, `-f.env`, `@file`), `git diff`/`git grep`의 파일 피연산자, `cd DIR && cat FILE` 복합 명령까지 확장했고, deny 대상 파일이 있는 디렉토리에 대한 `grep -r`/`cp -r`도 확인을 요구하도록 했습니다. 그러나 v2.1.260이 이 변경을 **롤백**했습니다 — `Read(./**/build/**)` deny 규칙이 걸린 프로젝트에서 모든 모드의 `npm run build`가 거부되고, `cd … && grep`이 auto mode에서조차 매번 프롬프트되는 부작용이 있었기 때문입니다. 따라서 현행(260+)에서는 `Read()` deny 규칙이 **Bash 인자까지는 커버하지 않습니다** — 이 파일에서 세 번째로 관측되는 "단일 릴리즈 권한 개선의 롤백" 사례이며(위 v2.1.232/233 「일반 교훈」 참조), 이런 개선은 여전히 defense-in-depth로만 취급합니다. 별도로 (259) 파싱 불가능한 managed settings(managed-settings 파일, drop-in, MDM plist, HKLM)를 만나면 이제 **무음으로 미강제** 처리하는 대신 **시작을 거부**하고 원인 소스를 명시합니다.
108
+
109
+ > **v2.1.260+**: 권한 규칙 정확성 수정 5건입니다. (a) 경로에 괄호가 포함된 `Edit`/`Write`/`Read` 규칙이 무효로 버려지거나 Bash 샌드박스에서 무시되어 "읽기 전용" 폴더가 실제로는 쓰기 가능했던 결함이 수정되었습니다. (b) 컴파일 불가능한 패턴(예: 닫히지 않은 `[`)을 가진 파일 규칙 하나가 모든 파일 편집을 `Invalid regular expression` 오류로 실패시키던 결함이 수정되어, 이제 그런 deny 규칙은 자신이 적은 리터럴 경로만 보호합니다. (c) zsh 전용 `REPORTTIME`/`REPORTMEMORY`/`DIRSTACKSIZE` 변수 대입 안에 command substitution을 숨긴 명령이 Bash 권한 검사를 자동 승인하던 결함이 수정되어 이제 프롬프트됩니다 — v2.1.221/238/257에 이은 4번째 zsh 파싱 하드닝이며, 이 저장소의 Bash 도구 실행 셸이 zsh이므로 직접 해당합니다. (d) 닫는 괄호 뒤에 텍스트가 붙어(`Bash(ls) x`) 결코 매칭되지 않던 permission rule이 무음 무시 대신 **invalid setting으로 보고**됩니다. (e) Glob/Grep이 권한 검사 **전에** 디스크에서 검색 경로를 프로브하던 결함이 수정되어, 이제 Read와 동일하게 권한 판정 후에 경로 부재가 보고됩니다(위 v2.1.251 (d)와 같은 계열). 또한 `!` bash-mode prompt 명령은 엄격 샌드박스 모드(`sandbox.allowUnsandboxedCommands: false`)에서도 이제 샌드박스 **밖에서** 실행됩니다 — R001/R015의 "사용자가 `!`로 직접 실행" 패턴이 설계상 명시적으로 unsandboxed임을 재확인시킵니다.
110
+
111
+ > **v2.1.265/267+**: (265) `.claude` 폴더 권한 옵션의 설명 문구가 실제 동작과 일치하도록 수정되었습니다 — 프로젝트의 `.claude` 폴더(또는 `~/.claude`) 파일을 해당 세션 동안 편집 허용한다는 뜻입니다. 또한 백슬래시가 포함된 플러그인 경로가 macOS/Linux에서 symlink containment 검사를 우회할 수 있던 결함이 수정되었습니다. (267) managed `allowedHttpHookUrls`, `httpHookAllowedEnvVars`, `allowedChannelPlugins` 설정이 읽기 불가 상태일 때 이제 **아무것도 허용하지 않도록**(이전에는 전부 허용) fail-closed로 바뀌었습니다 — 위 (259) managed-settings 파싱 실패 시 시작 거부 방향과 같은 계열입니다. fetched marketplace 항목 경로에 백슬래시가 포함되어 containment를 우회하던 결함도 함께 수정되었습니다. HTTP hook allowlist 항목은 R021(훅) 교차 참조.
112
+
107
113
  > **v2.1.238+**: Bash 도구의 permission 검사가 zsh 전용 조건문(shell conditional) 문법에 대해 추가로 개선되었습니다. 이는 위 v2.1.221 "zsh `[[ ]]` 정규식 조건문 안에서 숨겨진 명령이 권한 검사를 우회"의 **직접 연장선**입니다 — "개선"으로만 기술되어 있어 v2.1.221 수정이 완전 해결이 아니었거나 추가 우회 벡터가 있었음을 시사합니다. 이 저장소의 Bash 도구 실행 셸이 zsh이므로(R005 #1540 실측) 직접 관련됩니다.
108
114
 
109
115
  > **v2.1.246/248+**: (246) 끝에 매달린 `&&`/`||`가 있는 손상된(malformed) 명령에 대해 Bash 권한검사가 이제 **항상 승인을 요구**합니다 — 구버전에서는 이런 형태가 검사를 우회할 수 있었습니다. (248) `--restricted`(또는 `CLAUDE_CODE_RESTRICTED=1`) 모드가 신설되어 명령/코드 실행 도구와 `WebFetch`를 제거하고(`--tools`에 명시 시 예외), 파일 도구를 작업 디렉토리 내부로 제한하며, `bypassPermissions`를 거부하고, user/project/local settings 파일을 무시합니다. 이 저장소는 프로젝트 settings에 `bypassPermissions`를 선언하지만 v2.1.257부터 그 선언은 무시되므로(R010 Universal bypassPermissions의 ★ v2.1.257 노트 — 2026-09-02 실측 유효 모드는 user settings `auto`), `--restricted`와의 상호 배타성은 **user/managed scope에서 bypass를 켠 경우에 한해** 성립합니다 — 이 저장소 워크플로우에는 적용하지 않되, 신규 안전 모드 옵션으로 존재를 기록합니다.
@@ -41,6 +41,10 @@ The following git commands have caused working tree loss in past sessions (#1146
41
41
 
42
42
  > **v2.1.246/251+**: 자격증명 전송 경계 결함 2건이 수정되었습니다. (246) 서드파티 게이트웨이(`ANTHROPIC_BASE_URL`)용 API 키가 Anthropic 텔레메트리/메트릭 요청에 함께 실려 전송되던 결함 — 구버전에서는 게이트웨이 자격증명이 자기 호스트 밖으로 유출됐습니다. (251) `/ultrareview` 및 로컬 시딩 cloud session이 `prod.env` 계열·`*.tfvars` 파일, 또는 자격증명 파일의 에디터 swap/temp/backup 사본(`key.pem.tmp`, `id_rsa.swo`)을 업로드하던 결함 — 이제 로컬에 남습니다. 이 저장소는 `/ultrareview`를 사용하지 않으나, 두 항목 모두 이 섹션의 "자격증명 저장소 덤프 금지" 원칙과 동일한 위협 클래스에 대한 플랫폼 측 방어이므로 기록합니다.
43
43
 
44
+ > **v2.1.261+**: 위험한 `rm` 안전 프롬프트가 **positional parameter에 대한 `rm -rf`**(`rm -rf "$@"` 등)와 **큰따옴표로 감싼 `sh -c` 스크립트 내부**의 `rm -rf`까지 잡도록 확장되었습니다 — 위 Destructive Git Commands 표의 per-invocation 승인 요구와 동일한 원칙을 셸 레벨에서 보강하는 플랫폼 측 defense-in-depth이며, Pre-Delegation Blast-Radius Enumeration을 대체하지 않습니다. 또한 auto mode가 **공개 다이어그램 렌더러 URL에 다이어그램 소스를 실어 보내는 링크**(mermaid/kroki류 렌더 URL)를 그 사이트로의 **업로드**로 취급해, 사용자가 요청하지 않은 한 더 이상 auto-approve하지 않습니다. 이 저장소의 다이어그램 스킬(`eraser-diagrams`, mermaid 렌더링)이 이런 링크를 생성할 수 있으므로, 이런 링크에서 classifier가 멈추는 것은 정상 동작이지 오작동이 아닙니다 — 재시도하지 않습니다(cross-ref R010 Subagent Scope-Creep STOP Protocol).
45
+
46
+ > **v2.1.260/265/267+**: (260) `!` bash-mode 프롬프트에서 직접 입력한 명령은 strict sandbox mode에서도 샌드박스 **밖에서** 실행됩니다 — 즉 위 「Standing User-Deny + Classifier Block」섹션의 "`!`로 사용자에게 넘기는" 패턴은 설계상 **비샌드박스 경로**임을 명시적으로 인지해야 합니다. (265) macOS/Linux에서 백슬래시를 포함한 플러그인 경로가 symlink containment 검사를 우회하던 결함, (267) fetched marketplace entry 경로에 대한 동일 계열 결함이 각각 수정되었습니다 — 둘 다 v2.1.233 `\??\` device prefix 노트와 같은 **경로 표기 우회 계열**(같은 위치를 다르게 표기해 검사를 피함)입니다. 또한 (259) 동시 세션이 서로의 `~/.claude.json` 변경(workspace trust 초기화, MCP/project state 유실)을 조용히 되돌리던 결함도 수정되었습니다 — 공유 워크트리 다중 세션 실행 시 관련됩니다.
47
+
44
48
  ### Pre-Delegation Blast-Radius Enumeration
45
49
 
46
50
  > Origin: #1307 찐빠 #1 (High) — user chose "discard local changes and pull", and `git reset --hard origin/develop` was delegated immediately → user rejected (interrupt). The blast radius — that "discard local changes" included 18 files of *intended* uncommitted work (rule edits, new skills, new guides), not just a version downgrade — was never enumerated for the user.
@@ -256,6 +256,8 @@ Origin: #1584 #4 (v1.1.45 세션) — R021 자기 서술 staleness 반영을 R01
256
256
 
257
257
  > **v2.1.233+**: `claude plugin validate`가 **bare `.claude/skills` 디렉토리**(플러그인 매니페스트 없는 스킬 트리)도 검사해, frontmatter 파싱에 실패하는 `SKILL.md`를 보고합니다. 이 저장소의 `.claude/skills/**/SKILL.md`는 아래 Quick Verification Commands가 **개수만** 세고 frontmatter 유효성은 세지 않으므로, 스킬 추가·수정 후 `claude plugin validate`를 개수 대조와 **함께** 실행해 파싱 실패를 결정론적으로 잡습니다(구버전에서는 이 경로가 검사 대상이 아니어서 깨진 frontmatter가 런타임 미로드로만 드러났습니다). Cross-ref: R023(Tier 1 결정론적 검증).
258
258
 
259
+ > **v2.1.259/265+**: (259) `claude plugin validate --json`이 기계 판독 가능한 검증 리포트를 제공합니다 — 위 검사가 짝을 이루는 개수 대조와 함께 쓸 때는 이 리포트를 frontmatter 파싱 확인의 우선 수단으로 삼습니다. (259) 동시 세션이 서로의 `~/.claude.json` 변경(workspace trust 초기화, MCP/프로젝트 상태 유실)을 조용히 되돌리던 결함이 수정되었습니다 — 구버전의 공유 워크트리 다중 세션 실행(위 「게이트는 분기 시점 1회가 아니라 상태변경 위임마다」 참조)은 브랜치 상태뿐 아니라 trust/MCP 상태도 유실될 수 있었습니다. (265) Claude Code 자체의 git status·diff 프로브가 작업트리 내부의 **중첩 저장소**가 설정한 clean filter를 거쳐 실행되던 결함이 수정되었습니다 — 구버전에서는 중첩 저장소의 clean filter가 CC의 작업트리 인식을 셸에서 직접 실행한 `git status` 결과와 다르게 만들 수 있었으므로, 이 규칙이 의존하는 두 ground-truth 소스(CC 내부 관측 vs 셸 직접 실행)가 항상 일치한다고 보장되지 않았습니다.
260
+
259
261
  ## Quick Verification Commands — agent/skill/guide/wiki counts via ls/find/wc. See commands via Read tool.
260
262
 
261
263
  <!-- DETAIL: Quick Verification Commands
@@ -125,6 +125,17 @@ Origin: #1595 #5 (v1.1.48 세션 — R008 위반 3건이 단일 턴에 집중. t
125
125
  > **v2.1.174+**: Fixed the Workflow tool's `agent()` subagents missing per-agent attribution headers. Workflow-spawned subagents now carry attribution consistent with R008 — when authoring Workflow scripts, each `agent()` call is attributed like a direct Agent tool spawn. Align Workflow orchestration with the R008 `[agent][model] → Tool:` identification discipline: a Workflow `agent()` fan-out should still be reasoned about with the same per-agent identification model as parallel Agent tool spawns.
126
126
  -->
127
127
 
128
+ ## announce와 헤더는 narration이 아니라 visible text 블록으로 (Origin: #1654)
129
+
130
+ 모델 출력에는 `text` 블록과 **narration 블록**(트랜스크립트에 `type:"thinking"` + signature 라벨 `narration`으로 직렬화되는 사용자향 짧은 산문)이 있고, 한 API 메시지에는 **둘 중 하나만** 실린다(v1.1.61~62 세션 실측: 115메시지 중 공존 0). 도구 호출 턴을 narration 요약 한 문장("…했습니다. 이제 …하겠습니다")으로 시작하면 R007 헤더와 R008 접두사는 **어디에도 남지 않는다** — 실측: narration 47블록에 R007 헤더 0건, 대괄호 번호 항목 0건, Tool 표기 0건(조사 문장 인용 제외). advisor는 `type != "thinking"` 필터로 narration을 배제하므로 이 턴들은 전부 누락으로 계상되며, 실제로 v1.1.61 세션 advisory 18건은 **전부 진양성**이었다(직렬화 유실 가설은 advisor가 메시지 직후에 판정했다는 사실로 배제됨).
131
+
132
+ | Anti-pattern | Required |
133
+ |--------------|----------|
134
+ | 도구 호출 턴을 짧은 요약 산문만으로 시작(narration 채널로 흐름) | 헤더(`┌─ Agent:` 또는 단축 헤더)와 Core Rule 접두사를 **text 블록**으로 명시 — 산문 요약은 그 뒤에 |
135
+ | "announce를 썼다"는 기억으로 advisory를 오탐으로 가정 | 트랜스크립트의 `text` 블록에서 마커를 실측(R020 Self-Violation Counting) |
136
+
137
+ Iteration 1(Agent 스폰 15메시지 전부 narration)과 Iteration 2(7메시지 text)의 대비는 계수 도구 결함이 아니라 출력 채널 선택의 차이였다. 채널 선택 요인은 미귀속이다.
138
+
128
139
  ## Tier-3 Interaction Tool Prefix (MANDATORY)
129
140
 
130
141
  R008 "every tool call" applies to Tier-3 interaction tools too — NOT only file/exec tools. Applying the Core Rule prefix form (에이전트·모델 대괄호 다음 화살표와 Tool 표기) to Agent/Bash/Read while omitting it on `AskUserQuestion`, `TodoWrite`, `EnterPlanMode`, etc. is a violation.
@@ -92,6 +92,10 @@ Active removal of irrelevant retrieved content from agent context. Complements o
92
92
 
93
93
  > **v2.1.251+**: Sonnet 5의 기본 auto-compact 창이 **전체 1M 컨텍스트로 변경**되어, 1M 창 세션이 이제 ~934K가 아니라 ~967K 토큰에서 auto-compact됩니다. 위 `CLAUDE_CODE_DISABLE_1M_CONTEXT` 노트와 **직접 상호작용**합니다 — 그 env가 **설정된 환경**에서는 여전히 200K로 강제 유지되지만, **비활성 환경**(기본값)에서는 이번 변경으로 실효 auto-compact 임계값 자체가 상향됩니다. 이 저장소 에이전트 다수가 `claude-sonnet-5`이므로, 위 임계값 표의 백분율 계산은 env 설정 여부에 더해 이 CC 버전 여부까지 함께 확인해야 절대 토큰량이 정확합니다.
94
94
 
95
+ > **v2.1.260+**: 1M 컨텍스트를 가진 모델의 auto-compact 시점이 확대되어, Opus·Fable 세션도 이제 1M 토큰 한도 직전에 compact되고 매우 큰 컨텍스트에서의 recovery compaction이 더 이상 10분 타임아웃으로 실패하지 않습니다 — 위 v2.1.251 Sonnet 5 노트를 Opus/Fable 계열로 확장하는 것이므로, `CLAUDE_CODE_DISABLE_1M_CONTEXT`가 설정되지 않은 한 이 섹션의 백분율 임계값은 이제 세 모델군 전체에서 절대 토큰량 ~1M에 대응합니다. 같은 릴리즈에서 `/cost`와 statusline의 `prompt_cache` 필드가 prompt-cache miss의 **가능성 있는 원인**(도구 정의·시스템 프롬프트 변경, TTL 경과 idle)을 표시하도록 개선되어, 이 규칙이 다루는 캐시 관련 비용 이상 징후를 진단할 때 그 원인 후보를 출발점으로 삼을 수 있습니다 — 후보이지 확정 원인이 아니므로 R020 Proxy Signal 원칙대로 실측으로 확정합니다(cross-ref R012 statusline).
96
+
97
+ > **v2.1.261+**: 컨텍스트 비용 진단 도구 2종이 추가되었습니다. `/skill-doctor`는 로드된 스킬 중 사용되지 않는 것과 그 컨텍스트 비용을 표시해 가지치기 대상을 알려줍니다 — 이 저장소의 115개 스킬 열거 블록과 `profile` 스킬의 플러그인 세트 전환에 직접 관련됩니다. `bashOutputMaxChars`/`taskOutputMaxChars` 설정은 command·background-task 출력이 파일로 저장되기 전 모델에 인라인 전달되는 상한을 올릴 수 있습니다(최대 128K자). 지침: 이 상한을 기본값으로 올리지 않습니다 — 이 규칙의 압축 원칙(파일 목록 → 개수, 오류 트레이스 → 앞/뒤 줄)이 작은 인라인 출력을 선호하므로, pass/fail 라인이 잘리는 특정 검증에 한해서만 올리고 그 외에는 독립적인 exit-code 조회(R005 #1492)를 우선합니다.
98
+
95
99
  <!-- DETAIL: Context Budget Management
96
100
 
97
101
  Task-type-aware context thresholds that trigger ecomode earlier for context-heavy operations.
@@ -62,6 +62,8 @@ Countdown format: >=1d → "{d}d{h}h", >=1h → "{h}h{m}m", <1h → "{m}m", unav
62
62
  RL/WL segments omitted on CC older than v2.1.80.
63
63
  -->
64
64
 
65
+ > **v2.1.260+**: 상태줄의 `prompt_cache` 필드(및 `/cost`)가 이제 prompt-cache miss의 **가능성 있는 원인**을 함께 보여줍니다 — 예: 도구 정의나 시스템 프롬프트가 변경됨, TTL을 넘겨 유휴 상태였음. `.claude/statusline.sh`를 확장해 캐시 상태를 노출하려면 이 필드가 캐시 hit/miss 상태의 결정론적 소스입니다 — 함께 표시되는 원인은 플랫폼의 추정 후보이지 확정 원인이 아닙니다. cross-ref R013(context budget), R009(v2.1.229 prefix stagger — 병렬 배치에서 "도구 정의 변경됨"이 원인으로 뜨면 stagger의 캐시 재사용 이득이 소실됐다는 뜻입니다).
66
+
65
67
  ## Integration
66
68
 
67
69
  Integrates with R007 (Agent ID), R008 (Tool ID), R009 (Parallel).