oh-my-customcode 1.1.52 → 1.1.53

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.
@@ -10,7 +10,7 @@
10
10
  "command": "bash .claude/hooks/scripts/stage-blocker.sh"
11
11
  }
12
12
  ],
13
- "description": "Block Write/Edit tools during plan/verify/compound stages \u2014 only allow in implement stage"
13
+ "description": "Block Write/Edit tools during plan/verify/compound stages — only allow in implement stage"
14
14
  },
15
15
  {
16
16
  "matcher": "tool == \"Bash\" && tool_input.command matches \"(npm run dev|pnpm( run)? dev|yarn dev|bun run dev)\"",
@@ -70,7 +70,7 @@
70
70
  "command": "bash .claude/hooks/scripts/content-hash-validator.sh"
71
71
  }
72
72
  ],
73
- "description": "Validate file content hash before Edit \u2014 advisory staleness warning"
73
+ "description": "Validate file content hash before Edit — advisory staleness warning"
74
74
  },
75
75
  {
76
76
  "matcher": "tool == \"Write\" || tool == \"Edit\" || tool == \"Bash\"",
@@ -80,7 +80,7 @@
80
80
  "command": "bash .claude/hooks/scripts/schema-validator.sh"
81
81
  }
82
82
  ],
83
- "description": "Schema-based tool input validation \u2014 Phase 1 advisory only"
83
+ "description": "Schema-based tool input validation — Phase 1 advisory only"
84
84
  },
85
85
  {
86
86
  "matcher": "tool == \"Bash\"",
@@ -110,14 +110,14 @@
110
110
  "command": "bash .claude/hooks/scripts/rule-deletion-guard.sh"
111
111
  }
112
112
  ],
113
- "description": "Block rule file deletion \u2014 requires individual user confirmation per rule (R001 safety)"
113
+ "description": "Block rule file deletion — requires individual user confirmation per rule (R001 safety)"
114
114
  },
115
115
  {
116
116
  "matcher": "tool == \"Task\" || tool == \"Agent\"",
117
117
  "hooks": [
118
118
  {
119
119
  "type": "command",
120
- "command": "#!/bin/bash\ninput=$(cat)\nagent_type=$(echo \"$input\" | jq -r '.tool_input.subagent_type // \"unknown\"')\nmodel=$(echo \"$input\" | jq -r '.tool_input.model // \"inherit\"')\ndesc=$(echo \"$input\" | jq -r '.tool_input.description // \"\"' | head -c 40)\nresume=$(echo \"$input\" | jq -r '.tool_input.resume // empty')\nif [ -n \"$resume\" ]; then\n echo \"\u2500\u2500\u2500 [Resume] ${agent_type}:${model} | ${desc} \u2500\u2500\u2500\" >&2\nelse\n echo \"\u2500\u2500\u2500 [Spawn] ${agent_type}:${model} | ${desc} \u2500\u2500\u2500\" >&2\nfi\necho \"$input\""
120
+ "command": "#!/bin/bash\ninput=$(cat)\nagent_type=$(echo \"$input\" | jq -r '.tool_input.subagent_type // \"unknown\"')\nmodel=$(echo \"$input\" | jq -r '.tool_input.model // \"inherit\"')\ndesc=$(echo \"$input\" | jq -r '.tool_input.description // \"\"' | head -c 40)\nresume=$(echo \"$input\" | jq -r '.tool_input.resume // empty')\nif [ -n \"$resume\" ]; then\n echo \"─── [Resume] ${agent_type}:${model} | ${desc} ───\" >&2\nelse\n echo \"─── [Spawn] ${agent_type}:${model} | ${desc} ───\" >&2\nfi\necho \"$input\""
121
121
  },
122
122
  {
123
123
  "type": "command",
@@ -216,7 +216,7 @@
216
216
  "command": "bash .claude/hooks/scripts/user-prompt-preprocessor.sh"
217
217
  }
218
218
  ],
219
- "description": "Advisory pre-processing of user input \u2014 skill matching hints and intent analysis"
219
+ "description": "Advisory pre-processing of user input — skill matching hints and intent analysis"
220
220
  },
221
221
  {
222
222
  "matcher": "*",
@@ -226,7 +226,7 @@
226
226
  "command": "bash .claude/hooks/scripts/r007-r008-drift-advisor.sh"
227
227
  }
228
228
  ],
229
- "description": "Proactive R007/R008 drift advisory \u2014 checks last assistant turn for identification omission (#1229)"
229
+ "description": "Proactive R007/R008 drift advisory — checks last assistant turn for identification omission (#1229)"
230
230
  },
231
231
  {
232
232
  "matcher": "*",
@@ -246,7 +246,7 @@
246
246
  "command": "bash .claude/hooks/scripts/fail-axis-cause-advisor.sh"
247
247
  }
248
248
  ],
249
- "description": "Advisory on cause-free progress nudges when this session has logged tool failures \u2014 delivers via hookSpecificOutput.additionalContext (#1561)"
249
+ "description": "Advisory on cause-free progress nudges when this session has logged tool failures — delivers via hookSpecificOutput.additionalContext (#1561)"
250
250
  }
251
251
  ],
252
252
  "SubagentStart": [
@@ -255,7 +255,7 @@
255
255
  "hooks": [
256
256
  {
257
257
  "type": "command",
258
- "command": "#!/bin/bash\ninput=$(cat)\nagent_type=$(echo \"$input\" | jq -r '.agent_type // \"unknown\"')\nmodel=$(echo \"$input\" | jq -r '.model // \"inherit\"')\ndesc=$(echo \"$input\" | jq -r '.description // \"\"' | head -c 40)\necho \"\u2500\u2500\u2500 [SubagentStart] ${agent_type}:${model} | ${desc} \u2500\u2500\u2500\" >&2\necho \"$input\""
258
+ "command": "#!/bin/bash\ninput=$(cat)\nagent_type=$(echo \"$input\" | jq -r '.agent_type // \"unknown\"')\nmodel=$(echo \"$input\" | jq -r '.model // \"inherit\"')\ndesc=$(echo \"$input\" | jq -r '.description // \"\"' | head -c 40)\necho \"─── [SubagentStart] ${agent_type}:${model} | ${desc} ───\" >&2\necho \"$input\""
259
259
  },
260
260
  {
261
261
  "type": "command",
@@ -287,7 +287,7 @@
287
287
  },
288
288
  {
289
289
  "type": "prompt",
290
- "prompt": "A background subagent just completed. If it FAILED, do NOT auto-continue \u2014 report the failure and wait for user input."
290
+ "prompt": "A background subagent just completed. If it FAILED, do NOT auto-continue — report the failure and wait for user input."
291
291
  }
292
292
  ],
293
293
  "description": "Record agent outcomes + auto-continue workflow + R007/R008 drift advisory on subagent completion (autonomous-loop re-entry, #1545)"
@@ -314,7 +314,7 @@
314
314
  "command": "bash .claude/hooks/scripts/file-change-validator.sh"
315
315
  }
316
316
  ],
317
- "description": "Validate externally modified files \u2014 advisory staleness warning (v2.1.83+)"
317
+ "description": "Validate externally modified files — advisory staleness warning (v2.1.83+)"
318
318
  }
319
319
  ],
320
320
  "Notification": [
@@ -335,7 +335,7 @@
335
335
  "hooks": [
336
336
  {
337
337
  "type": "command",
338
- "command": "#!/bin/bash\ninput=$(cat)\nteammate=$(echo \"$input\" | jq -r '.teammate_id // \"unknown\"' 2>/dev/null)\necho \"[Hook] TeammateIdle: $teammate \u2014 available for reassignment\" >&2\necho \"$input\""
338
+ "command": "#!/bin/bash\ninput=$(cat)\nteammate=$(echo \"$input\" | jq -r '.teammate_id // \"unknown\"' 2>/dev/null)\necho \"[Hook] TeammateIdle: $teammate — available for reassignment\" >&2\necho \"$input\""
339
339
  }
340
340
  ],
341
341
  "description": "Track idle Agent Teams members for work reassignment"
@@ -347,7 +347,7 @@
347
347
  "hooks": [
348
348
  {
349
349
  "type": "command",
350
- "command": "#!/bin/bash\ninput=$(cat)\ntask_id=$(echo \"$input\" | jq -r '.task_id // \"\"' 2>/dev/null)\ndesc=$(echo \"$input\" | jq -r '.description // \"\"' 2>/dev/null | head -c 60)\necho \"[Hook] TaskCreated: $task_id \u2014 $desc\" >&2\necho \"$input\""
350
+ "command": "#!/bin/bash\ninput=$(cat)\ntask_id=$(echo \"$input\" | jq -r '.task_id // \"\"' 2>/dev/null)\ndesc=$(echo \"$input\" | jq -r '.description // \"\"' 2>/dev/null | head -c 60)\necho \"[Hook] TaskCreated: $task_id — $desc\" >&2\necho \"$input\""
351
351
  }
352
352
  ],
353
353
  "description": "Log task creation events for Agent Teams coordination"
@@ -359,7 +359,7 @@
359
359
  "hooks": [
360
360
  {
361
361
  "type": "command",
362
- "command": "#!/bin/bash\ninput=$(cat)\ntask_id=$(echo \"$input\" | jq -r '.task_id // \"\"' 2>/dev/null)\nresult=$(echo \"$input\" | jq -r '.result // \"\"' 2>/dev/null | head -c 60)\necho \"[Hook] TaskCompleted: $task_id \u2014 $result\" >&2\necho \"$input\""
362
+ "command": "#!/bin/bash\ninput=$(cat)\ntask_id=$(echo \"$input\" | jq -r '.task_id // \"\"' 2>/dev/null)\nresult=$(echo \"$input\" | jq -r '.result // \"\"' 2>/dev/null | head -c 60)\necho \"[Hook] TaskCompleted: $task_id — $result\" >&2\necho \"$input\""
363
363
  }
364
364
  ],
365
365
  "description": "Log task completion events for Agent Teams coordination"
@@ -374,7 +374,7 @@
374
374
  "command": "bash .claude/hooks/scripts/task-state-precompact.sh"
375
375
  }
376
376
  ],
377
- "description": "Serialize active task state before compaction \u2014 pairs with PostCompact restore (#922)"
377
+ "description": "Serialize active task state before compaction — pairs with PostCompact restore (#922)"
378
378
  }
379
379
  ],
380
380
  "PostCompact": [
@@ -383,10 +383,10 @@
383
383
  "hooks": [
384
384
  {
385
385
  "type": "prompt",
386
- "prompt": "Context compacted. RULES ACTIVE.\n\nR007: next response MUST start with \u250c\u2500 Agent: claude (default) / \u2514\u2500 Task: {task}\nR008: before EVERY tool call \u2192 [claude][model] \u2192 Tool: Name / Target: path\nR010: write\u2192delegate to subagent; orchestrator read-only (Read/Glob/Grep); git\u2192mgr-gitnerd\nR009: 2+ independent tasks \u2192 parallel agents, same message\nR018: 3+ agents OR review cycle \u2192 Agent Teams\nR000: user communication in Korean (\ud55c\uad6d\uc5b4); code/files in English \u2014 NON-NEGOTIABLE\nR001: prohibited actions (no secrets, no system files, no rm -rf); stop on violation \u2192 preserve state \u2192 report \u2192 wait\nR002: tool tiers (Read/Grep=free, Write/Edit=notify, Bash=approval); no writes outside project; no .env/.git/config\n\nAutonomous mode: if /tmp/.claude-autonomous-$PPID exists \u2192 simple git (add/commit/push) may run directly; Write/Edit still delegates.\nbypassPermissions does NOT persist across compaction \u2014 re-enable if tasks seem blocked.\n\nTask state restore: if /tmp/.claude-task-state-$PPID.json exists, read it to recover active pipeline, dev stage, and autonomous mode state. This was saved by PreCompact hook.\n\nRe-read CLAUDE.md NOW."
386
+ "prompt": "Context compacted. RULES ACTIVE.\n\nR007: next response MUST start with ┌─ Agent: claude (default) / └─ Task: {task}\nR008: before EVERY tool call → [claude][model] → Tool: Name / Target: path\nR010: write→delegate to subagent; orchestrator read-only (Read/Glob/Grep); git→mgr-gitnerd\nR009: 2+ independent tasks → parallel agents, same message\nR018: 3+ agents OR review cycle → Agent Teams\nR000: user communication in Korean (한국어); code/files in English — NON-NEGOTIABLE\nR001: prohibited actions (no secrets, no system files, no rm -rf); stop on violation → preserve state → report → wait\nR002: tool tiers (Read/Grep=free, Write/Edit=notify, Bash=approval); no writes outside project; no .env/.git/config\n\nAutonomous mode: if /tmp/.claude-autonomous-$PPID exists → simple git (add/commit/push) may run directly; Write/Edit still delegates.\nbypassPermissions does NOT persist across compaction — re-enable if tasks seem blocked.\n\nTask state restore: if /tmp/.claude-task-state-$PPID.json exists, read it to recover active pipeline, dev stage, and autonomous mode state. This was saved by PreCompact hook.\n\nRe-read CLAUDE.md NOW."
387
387
  }
388
388
  ],
389
- "description": "Reinforce enforced rules after context compaction \u2014 prevents rule amnesia (v2.1.76+)"
389
+ "description": "Reinforce enforced rules after context compaction — prevents rule amnesia (v2.1.76+)"
390
390
  }
391
391
  ],
392
392
  "PostToolUse": [
@@ -473,7 +473,7 @@
473
473
  "continueOnBlock": true
474
474
  }
475
475
  ],
476
- "description": "Context budget advisor \u2014 track tool usage patterns and advise ecomode activation"
476
+ "description": "Context budget advisor — track tool usage patterns and advise ecomode activation"
477
477
  },
478
478
  {
479
479
  "matcher": "tool == \"Edit\" || tool == \"Write\" || tool == \"Bash\" || tool == \"Task\" || tool == \"Agent\" || tool == \"Read\" || tool == \"Glob\" || tool == \"Grep\"",
@@ -484,7 +484,7 @@
484
484
  "continueOnBlock": true
485
485
  }
486
486
  ],
487
- "description": "Proactive R007/R008 drift advisory on tool use \u2014 covers the orchestrator-only stretch before the first subagent spawn, where neither UserPromptSubmit nor SubagentStop fires (#1553). Turn-deduplicated; exit 0 advisory only."
487
+ "description": "Proactive R007/R008 drift advisory on tool use — covers the orchestrator-only stretch before the first subagent spawn, where neither UserPromptSubmit nor SubagentStop fires (#1553). Turn-deduplicated; exit 0 advisory only."
488
488
  },
489
489
  {
490
490
  "matcher": "tool == \"Edit\" || tool == \"Write\" || tool == \"Bash\" || tool == \"Task\" || tool == \"Agent\"",
@@ -506,7 +506,7 @@
506
506
  "continueOnBlock": true
507
507
  }
508
508
  ],
509
- "description": "Advisory cost cap monitoring \u2014 warn when session cost approaches configurable limit"
509
+ "description": "Advisory cost cap monitoring — warn when session cost approaches configurable limit"
510
510
  },
511
511
  {
512
512
  "matcher": "tool == \"Read\"",
@@ -516,7 +516,7 @@
516
516
  "command": "bash .claude/hooks/scripts/content-hash-validator.sh"
517
517
  }
518
518
  ],
519
- "description": "Store content hashes for Read operations \u2014 enables Edit staleness detection"
519
+ "description": "Store content hashes for Read operations — enables Edit staleness detection"
520
520
  },
521
521
  {
522
522
  "matcher": "tool == \"Bash\" || tool == \"Read\" || tool == \"Grep\"",
@@ -526,7 +526,7 @@
526
526
  "command": "bash .claude/hooks/scripts/secret-filter.sh"
527
527
  }
528
528
  ],
529
- "description": "Detect potential secrets in Bash/Read/Grep output \u2014 advisory warning only"
529
+ "description": "Detect potential secrets in Bash/Read/Grep output — advisory warning only"
530
530
  },
531
531
  {
532
532
  "matcher": "tool == \"Edit\" || tool == \"Write\" || tool == \"Bash\" || tool == \"Agent\"",
@@ -567,7 +567,7 @@
567
567
  "command": "bash .claude/hooks/scripts/auto-dev-token-tracker.sh"
568
568
  }
569
569
  ],
570
- "description": "auto-dev token spend tracker \u2014 records per-phase token estimates during /pipeline auto-dev (Issue #1057, advisory)"
570
+ "description": "auto-dev token spend tracker — records per-phase token estimates during /pipeline auto-dev (Issue #1057, advisory)"
571
571
  }
572
572
  ],
573
573
  "Stop": [
@@ -621,16 +621,6 @@
621
621
  ],
622
622
  "description": "Detect reusable skill candidates from session task outcomes (advisory, exit 0)"
623
623
  },
624
- {
625
- "matcher": "*",
626
- "hooks": [
627
- {
628
- "type": "prompt",
629
- "prompt": "Session-end memory checkpoint (R011 enforcement). Check if the session-end memory save workflow was completed: 1) sys-memory-keeper was delegated to update MEMORY.md. Note: episodic-memory auto-indexes after session \u2014 no manual action needed. Decision rules (evaluate in order): 1. If session had no explicit session-end signal (quick question, no memory work): APPROVE immediately. 2. If the step was already attempted (success or failure both count): APPROVE. 3. If the step was NOT yet performed: PERFORM IT NOW (maximum 1 attempt): a. Delegate to sys-memory-keeper to update MEMORY.md \u2014 call Agent tool with sys-memory-keeper. b. After completion (success or failure), APPROVE stop. IMPORTANT: Do NOT block indefinitely. After 1 attempt, always APPROVE. IMPORTANT: Do NOT loop \u2014 if you already performed this step in this same stop hook evaluation, APPROVE."
630
- }
631
- ],
632
- "description": "Enforce R011 session-end memory saves \u2014 auto-perform if not yet done (episodic-memory auto-indexes)"
633
- },
634
624
  {
635
625
  "matcher": "*",
636
626
  "hooks": [
@@ -651,7 +641,7 @@
651
641
  "command": "bash .claude/hooks/scripts/failure-ledger.sh"
652
642
  }
653
643
  ],
654
- "description": "FAIL-axis instrumentation \u2014 append tool failures to the error ledger (JSONL). Never blocks; feeds fail-axis-cause-advisor.sh (#1561)"
644
+ "description": "FAIL-axis instrumentation — append tool failures to the error ledger (JSONL). Never blocks; feeds fail-axis-cause-advisor.sh (#1561)"
655
645
  }
656
646
  ]
657
647
  }
@@ -21,9 +21,15 @@ oh-my-customcode uses an **advisory-first enforcement model**. Most rules are en
21
21
  | Advisory (proactive) | UserPromptSubmit hook | R020 (원인 진단) | `fail-axis-cause-advisor.sh` (#1561, v1.1.44) — 원장에 실패 기록이 있는데 원인 진술 없는 재촉 프롬프트가 오면 `hookSpecificOutput.additionalContext`로 "원인 가설 되묻기" advisory 전달. 원장 부재 시 조용히 통과 |
22
22
  | Prompt-based | CLAUDE.md + rules/ + `SessionStart` 재주입(matcher `*`; PostCompact 이벤트 배선은 유지되나 효과 미보장[^postcompact]) | All MUST rules | Behavioral guidance in context |
23
23
 
24
- > **Advisory (proactive/retroactive) 발화 결함과 해소 (실측)**: `hookSpecificOutput.additionalContext` **전달 경로 자체는 #1547(v1.1.40)에서 구현**됐으나, 그 앞단 **파서 셀렉터 결함**으로 advisory가 **v1.1.42까지 한 번도 발화하지 못했다** — `jq -r '.role'`로 읽었으나 트랜스크립트 최상위에 `role` 키가 없어(실제는 `.message.role`) `last_assistant`가 항상 비고 즉시 `exit 0`으로 종료됐다. 당시 실측: 트랜스크립트 771개 전수에서 `"additionalContext":` 출현 0건, 라이브 프로브 stdout/stderr 각 0바이트. **proactive(`r007-r008-drift-advisor.sh`)와 retroactive(`session-reflection.sh`, 동일 결함) 두 계층 모두 미발화**였다. **v1.1.43에서 양 계층 파서 복구 + `PostToolUse` 배선을 완료했고, 라이브 프로브로 최초 발화를 확인했다(#1553).** 후속으로 v1.1.44에서 R008 판정을 블록 인접 비교 → 턴 단위 개수 비교로 전환(#1563), v1.1.45에서 Skill 도구 면제를 추가했다(#1569). **v1.1.49에서 역방향 신호**(announce > tool_use — 도구 호출을 예고해 놓고 tool_use 블록 없이 턴을 종료한 방향)**를 추가했다(#1595 #6)** — 기존 판정식 `max(0, tool_use − announce)`는 이 방향을 **구조적으로 0으로 처리**해 원리적으로 탐지 불가였다. 역방향은 **전용 앵커 정규식**(`$an_anchored`, 줄 시작 앵커 있음 — forward의 `$an_tool`에는 적용하지 않는다. forward는 announce를 덜 세면 위반이 **늘어나기** 때문)을 쓰고, **Skill 포함 전체 tool_use가 0건**일 때만 계상한다(Skill 제외 카운트를 쓰면 Skill만 호출한 준수 턴에서 오발화). 기본 off 옵트인(`OMCUSTOM_R008_REVERSE=on`으로 활성)이다 — 482턴 실측에서 순진한 `announce − ntools > 0` 구현은 36턴에 발화해 advisory 총량을 2배로 만들었고(16건은 Skill 제외 아티팩트, 15건은 앵커 없는 정규식의 산문 매칭), 협소화 후 3/3 진양성·오탐 0(앵커 비용은 실제 announce 969줄 중 1줄, 0.1%)이 되었으나 표본이 3건이라 기본 활성은 보류했다. **배선 구조상 예방 효과가 없다는 점도 보류 근거다** — 결함 턴은 tool_use가 0이라 `PostToolUse`·`SubagentStop`이 발화하지 않고, `UserPromptSubmit`은 사용자가 이미 개입한 뒤 발화한다. 정시에 발화하는 유일한 이벤트는 `Stop`이며 거기 걸린 훅은 `session-reflection.sh`다. 역방향은 그래서 **의도적으로 advisor 전용**이며 `session-reflection.sh`에는 복제하지 않았다(같은 결함을 두 번 보고하면서 교정 기회는 여전히 0이 되고, 되돌릴 지점만 두 곳이 된다).
24
+ > **훅 배선 경로 (v1.1.53, #1623)**: `.claude/hooks/hooks.json`은 CC가 로드하는 파일이 아니다 — CC 2.1.251 바이너리에 `.claude/hooks` 경로 참조가 0건이고, `settings.json`에 `hooks` 키가 이력상 존재한 적이 없었다. 컴파일레이션 메타포로는 `hooks.json` = **소스**(source), CC가 실제 로드하는 것은 `.claude/settings.json`(+`.local.json`, +`templates/` 미러)의 공식 `hooks` 블록 = **빌드 산출물**이며, 변환은 `src/core/hooks-settings.ts`가 담당한다(matcher 조건 DSL 12건은 스크립트 자체 가드 3건 + stdin-가드 래퍼 9건으로 이관). `omcustom init` 사용자는 `installHooksSettings()`가 최종 settings.local.json에 병합하는 경로를 거친다. **`hooks.json`만 고치고 settings 재생성(빌드)을 누락하면 변경이 발화에 반영되지 않는다** — R022 Wiki Sync의 "페이지 갱신 + 매니페스트 재시딩" 이원 요구와 동형이다. 라이브 실증(2026-08-29 새 세션 프로브): SessionStart hook_success 9건(플러그인 2 + 프로젝트 7), `[claude-md-reinject]` 마커 5히트, UserPromptSubmit·Stop 발화 확인 — **세션 단위 발화의 최초 실측**은 이 릴리즈다.
25
+
26
+ | Anti-pattern | Required |
27
+ |--------------|----------|
28
+ | `hooks.json`만 편집하고 커밋 → settings.json `hooks` 블록 미재생성 | 편집 후 `hooks-settings.ts` 변환기(빌드) 실행 → settings.json(+local+templates) 재생성 확인 후 커밋 |
29
+
30
+ > **Advisory (proactive/retroactive) 발화 결함과 해소 (실측)**: `hookSpecificOutput.additionalContext` **전달 경로 자체는 #1547(v1.1.40)에서 구현**됐으나, 그 앞단 **파서 셀렉터 결함**으로 advisory가 **v1.1.42까지 한 번도 발화하지 못했다** — `jq -r '.role'`로 읽었으나 트랜스크립트 최상위에 `role` 키가 없어(실제는 `.message.role`) `last_assistant`가 항상 비고 즉시 `exit 0`으로 종료됐다. 당시 실측: 트랜스크립트 771개 전수에서 `"additionalContext":` 출현 0건, 라이브 프로브 stdout/stderr 각 0바이트. **proactive(`r007-r008-drift-advisor.sh`)와 retroactive(`session-reflection.sh`, 동일 결함) 두 계층 모두 미발화**였다. **v1.1.43에서 양 계층 파서 복구 + `PostToolUse` 배선을 완료했고, 라이브 프로브로 최초 발화를 확인했다(#1553).** **재검토(#1623)**: 이 서술은 세션 단위 CC 훅 서브시스템 경유 증거가 없다 — 당시에도 CC가 로드하는 `settings.json`에 `hooks` 블록이 부재했으므로, 리서치(git 이력 전수) 판단으로는 스크립트에 synthetic stdin을 직접 주입한 검증이었을 가능성이 가장 유력하다(단정 금지 — "직접 증거 없음"으로 기록). 세션 단위 발화의 최초 실측은 위 「훅 배선 경로 (v1.1.53, #1623)」의 v1.1.53이다. 후속으로 v1.1.44에서 R008 판정을 블록 인접 비교 → 턴 단위 개수 비교로 전환(#1563), v1.1.45에서 Skill 도구 면제를 추가했다(#1569). **v1.1.49에서 역방향 신호**(announce > tool_use — 도구 호출을 예고해 놓고 tool_use 블록 없이 턴을 종료한 방향)**를 추가했다(#1595 #6)** — 기존 판정식 `max(0, tool_use − announce)`는 이 방향을 **구조적으로 0으로 처리**해 원리적으로 탐지 불가였다. 역방향은 **전용 앵커 정규식**(`$an_anchored`, 줄 시작 앵커 있음 — forward의 `$an_tool`에는 적용하지 않는다. forward는 announce를 덜 세면 위반이 **늘어나기** 때문)을 쓰고, **Skill 포함 전체 tool_use가 0건**일 때만 계상한다(Skill 제외 카운트를 쓰면 Skill만 호출한 준수 턴에서 오발화). 기본 off 옵트인(`OMCUSTOM_R008_REVERSE=on`으로 활성)이다 — 482턴 실측에서 순진한 `announce − ntools > 0` 구현은 36턴에 발화해 advisory 총량을 2배로 만들었고(16건은 Skill 제외 아티팩트, 15건은 앵커 없는 정규식의 산문 매칭), 협소화 후 3/3 진양성·오탐 0(앵커 비용은 실제 announce 969줄 중 1줄, 0.1%)이 되었으나 표본이 3건이라 기본 활성은 보류했다. **배선 구조상 예방 효과가 없다는 점도 보류 근거다** — 결함 턴은 tool_use가 0이라 `PostToolUse`·`SubagentStop`이 발화하지 않고, `UserPromptSubmit`은 사용자가 이미 개입한 뒤 발화한다. 정시에 발화하는 유일한 이벤트는 `Stop`이며 거기 걸린 훅은 `session-reflection.sh`다. 역방향은 그래서 **의도적으로 advisor 전용**이며 `session-reflection.sh`에는 복제하지 않았다(같은 결함을 두 번 보고하면서 교정 기회는 여전히 0이 되고, 되돌릴 지점만 두 곳이 된다).
25
31
  >
26
- > 교훈: **배선 확인 ≠ 전달 확인 ≠ 발화 확인** — R020 "actual outcome ≠ attempt"의 훅 도메인 재현 사례.
32
+ > 교훈: **배선 확인 ≠ 전달 확인 ≠ 발화 확인 ≠ 로드 확인** — R020 "actual outcome ≠ attempt"의 훅 도메인 재현 사례. v1.1.53(#1623)이 드러낸 것은 이 셋보다 앞선 **제4층**이다 — 배선 파일(`hooks.json`) 자체가 CC에 **로드되지 않았다면** 배선·전달·발화 확인은 전부 무의미한 층 위에서 이루어진 것이다.
27
33
 
28
34
  <!--
29
35
  > **v2.1.163+**: Stop and SubagentStop hooks can return `hookSpecificOutput.additionalContext` (JSON) to feed structured feedback back into Claude's context without triggering a hook error label. This enables advisory-style enforcement via Stop/SubagentStop hooks (e.g., `session-reflection.sh`, omcustom-loop SubagentStop) to pass richer context — replacing plain stderr text — without disrupting the turn continuation behavior that advisory-first enforcement relies on.