oh-my-customcode 1.1.39 → 1.1.41
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.
- package/dist/cli/index.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/templates/.claude/hooks/scripts/r007-r008-drift-advisor.sh +42 -14
- package/templates/.claude/rules/MUST-enforcement-policy.md +3 -3
- package/templates/.github/workflows/cc-release-monitor.yml +4 -0
- package/templates/manifest.json +1 -1
package/dist/cli/index.js
CHANGED
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
# r007-r008-drift-advisor.sh — PROACTIVE R007/R008 drift advisory (#1229, #1545)
|
|
2
|
+
# r007-r008-drift-advisor.sh — PROACTIVE R007/R008 drift advisory (#1229, #1545, #1547)
|
|
3
3
|
#
|
|
4
4
|
# Wired to TWO trigger points:
|
|
5
5
|
# 1. UserPromptSubmit — fires before Claude responds to a user-typed prompt (#1229).
|
|
@@ -10,51 +10,71 @@
|
|
|
10
10
|
#
|
|
11
11
|
# Inspects the LAST completed assistant turn in the session transcript for R007/R008
|
|
12
12
|
# compliance BEFORE Claude responds. If the previous turn drifted (missing identification
|
|
13
|
-
# header / tool prefix),
|
|
13
|
+
# header / tool prefix), delivers an advisory so the upcoming response self-corrects.
|
|
14
14
|
#
|
|
15
15
|
# This is the PROACTIVE complement to the retroactive session-reflection.sh (Stop hook, #1190).
|
|
16
16
|
# Detection patterns are reused from session-reflection.sh.
|
|
17
17
|
#
|
|
18
|
-
# Advisory-only: ALWAYS exits 0,
|
|
18
|
+
# Advisory-only: ALWAYS exits 0, NEVER blocks.
|
|
19
19
|
# Performance: parses ONLY the last assistant turn (not the whole transcript).
|
|
20
20
|
# Input-schema note: session_id/transcript_path are COMMON fields present on both
|
|
21
21
|
# UserPromptSubmit and SubagentStop hook payloads, so the detection logic below is
|
|
22
22
|
# event-agnostic and required no functional changes for the SubagentStop wiring.
|
|
23
23
|
#
|
|
24
|
+
# Delivery mechanism (#1547 fix):
|
|
25
|
+
# Prior to this fix, the advisory was written to stderr with exit 0. Per the official
|
|
26
|
+
# Claude Code hook spec, stderr on exit 0 is NEVER fed into the model's context for ANY
|
|
27
|
+
# hook event (it is only visible in transcript debug mode, i.e. to a human, not Claude) —
|
|
28
|
+
# so #1545's SubagentStop wiring never actually reached the model despite firing correctly.
|
|
29
|
+
# The confirmed non-blocking delivery path for BOTH UserPromptSubmit and SubagentStop is
|
|
30
|
+
# `hookSpecificOutput.additionalContext` in JSON stdout with exit 0:
|
|
31
|
+
# {"hookSpecificOutput": {"hookEventName": "<event>", "additionalContext": "<text>"}}
|
|
32
|
+
# This is NOT the same as `"decision": "block"` — that would force Stop/SubagentStop to
|
|
33
|
+
# block (refuse to stop), which is exactly the blocking behavior R021 (advisory-first
|
|
34
|
+
# enforcement) forbids for this hook. additionalContext alone (no `decision` field) is
|
|
35
|
+
# non-blocking: Claude is allowed to stop/continue normally and simply sees the extra
|
|
36
|
+
# context on its next turn. Exit code MUST stay 0 — exit 2 causes Claude Code to discard
|
|
37
|
+
# any JSON output and treat stderr as a blocking error instead (see Common JSON Fields /
|
|
38
|
+
# Exit Code Behavior in the official hook reference).
|
|
39
|
+
# The stderr line is kept for human-visible audit trail (harmless on exit 0) but is no
|
|
40
|
+
# longer the delivery mechanism.
|
|
41
|
+
#
|
|
24
42
|
# 환경변수 override (테스트/디버깅용):
|
|
25
43
|
# OMCUSTOM_R007_ADVISOR=off — advisory 완전 비활성화 (pass-through)
|
|
26
44
|
# OMCUSTOM_TRANSCRIPT_BASE — transcript 디렉토리 경로 override
|
|
27
45
|
|
|
28
46
|
set -euo pipefail
|
|
29
47
|
|
|
30
|
-
# ──
|
|
48
|
+
# ── stdin 읽기 ──
|
|
31
49
|
input=$(cat)
|
|
32
50
|
|
|
33
51
|
# ── Opt-out 체크 ──
|
|
34
52
|
if [ "${OMCUSTOM_R007_ADVISOR:-}" = "off" ]; then
|
|
35
|
-
echo "$input"
|
|
36
53
|
exit 0
|
|
37
54
|
fi
|
|
38
55
|
|
|
39
56
|
# ── jq 의존성 체크 ──
|
|
40
57
|
if ! command -v jq >/dev/null 2>&1; then
|
|
41
|
-
echo "$input"
|
|
42
58
|
exit 0
|
|
43
59
|
fi
|
|
44
60
|
|
|
45
61
|
# ── session_id 추출 ──
|
|
46
62
|
session_id=$(echo "$input" | jq -r '.session_id // empty' 2>/dev/null)
|
|
47
63
|
if [ -z "$session_id" ]; then
|
|
48
|
-
echo "$input"
|
|
49
64
|
exit 0
|
|
50
65
|
fi
|
|
51
66
|
|
|
67
|
+
# ── hook_event_name 추출 (hookSpecificOutput.hookEventName에 되돌려줄 값) ──
|
|
68
|
+
hook_event_name=$(echo "$input" | jq -r '.hook_event_name // empty' 2>/dev/null)
|
|
69
|
+
if [ -z "$hook_event_name" ]; then
|
|
70
|
+
hook_event_name="UserPromptSubmit"
|
|
71
|
+
fi
|
|
72
|
+
|
|
52
73
|
# ── 경로 결정 (환경변수 override 지원) ──
|
|
53
74
|
TRANSCRIPT_BASE="${OMCUSTOM_TRANSCRIPT_BASE:-${HOME}/.claude/projects/-Users-sangyi-workspace-projects-oh-my-customcode}"
|
|
54
75
|
TRANSCRIPT_PATH="${TRANSCRIPT_BASE}/${session_id}.jsonl"
|
|
55
76
|
|
|
56
77
|
if [ ! -f "$TRANSCRIPT_PATH" ]; then
|
|
57
|
-
echo "$input"
|
|
58
78
|
exit 0
|
|
59
79
|
fi
|
|
60
80
|
|
|
@@ -70,7 +90,6 @@ while IFS= read -r line; do
|
|
|
70
90
|
done < <(tail -r "$TRANSCRIPT_PATH" 2>/dev/null || tac "$TRANSCRIPT_PATH" 2>/dev/null)
|
|
71
91
|
|
|
72
92
|
if [ -z "$last_assistant" ]; then
|
|
73
|
-
echo "$input"
|
|
74
93
|
exit 0
|
|
75
94
|
fi
|
|
76
95
|
|
|
@@ -116,12 +135,21 @@ while [ "$i" -lt "$content_length" ]; do
|
|
|
116
135
|
i=$((i+1))
|
|
117
136
|
done
|
|
118
137
|
|
|
119
|
-
# ── advisory
|
|
138
|
+
# ── advisory 전달 (위반 시에만) ──
|
|
120
139
|
if [ "$r007_violations" -gt 0 ] || [ "$r008_violations" -gt 0 ]; then
|
|
121
|
-
printf '[R007/R008 Advisory] 직전 응답에서 식별 누락 감지 (R007 헤더=%d, R008 접두사=%d). 이번 응답은 ┌─ Agent: 헤더로 시작하고, 모든 도구 호출에 [agent][model] → Tool: 접두사를
|
|
122
|
-
"$r007_violations" "$r008_violations"
|
|
140
|
+
advisory_text=$(printf '[R007/R008 Advisory] 직전 응답에서 식별 누락 감지 (R007 헤더=%d, R008 접두사=%d). 이번 응답은 ┌─ Agent: 헤더로 시작하고, 모든 도구 호출에 [agent][model] → Tool: 접두사를 포함하십시오.' \
|
|
141
|
+
"$r007_violations" "$r008_violations")
|
|
142
|
+
|
|
143
|
+
# 사람이 보는 감사 추적용 (exit 0에서는 모델에 전달되지 않음 — #1547 참고)
|
|
144
|
+
printf '%s\n' "$advisory_text" >&2
|
|
145
|
+
|
|
146
|
+
# #1547 fix: hookSpecificOutput.additionalContext로 모델 컨텍스트에 실제 전달.
|
|
147
|
+
# decision 필드는 절대 포함하지 않는다 — "block"을 쓰면 Stop/SubagentStop 정지를
|
|
148
|
+
# 강제로 막아버려 advisory-only 원칙(R021)을 위반하게 된다. exit code는 반드시 0.
|
|
149
|
+
jq -cn --arg event "$hook_event_name" --arg ctx "$advisory_text" \
|
|
150
|
+
'{hookSpecificOutput: {hookEventName: $event, additionalContext: $ctx}}'
|
|
123
151
|
fi
|
|
124
152
|
|
|
125
|
-
# ──
|
|
126
|
-
|
|
153
|
+
# ── 위반이 없으면 아무것도 출력하지 않는다 (오탐 방지) ──
|
|
154
|
+
# ── 항상 exit 0 (advisory는 절대 차단 금지) ──
|
|
127
155
|
exit 0
|
|
@@ -14,7 +14,7 @@ oh-my-customcode uses an **advisory-first enforcement model**. Most rules are en
|
|
|
14
14
|
| Soft Block | Stop hook prompt | R011 session-end saves | Auto-performs then approves |
|
|
15
15
|
| Conversation Block | PostToolUse hook + `continueOnBlock` (CC v2.1.139+), exit 2 | stuck-detector, context-budget-advisor, cost-cap-advisor | Feeds rejection reason into conversation; Claude continues with awareness |
|
|
16
16
|
| Advisory | PostToolUse hooks | R007, R008, R009, R010, R018 | Warns via stderr, never blocks |
|
|
17
|
-
| Advisory (proactive) | UserPromptSubmit
|
|
17
|
+
| Advisory (proactive) | UserPromptSubmit + SubagentStop hooks | R007, R008 (`r007-r008-drift-advisor.sh`, #1229 UserPromptSubmit; #1545 added SubagentStop) | Reads last assistant turn; emits stderr advisory if header/prefix absent. SubagentStop wiring (#1545) closes a structural gap: UserPromptSubmit fires only on user input, so long-lived autonomous loops (e.g. `/fsd`) that re-enter via task notifications (no user input) never reached the advisor — measured 2026-07-30: 6 responses / 12 tool calls with missing R007/R008 and zero advisory fires. Complements retroactive Stop-hook (`session-reflection.sh`, #1190). **Delivery gap resolved (#1547, v1.1.40)**: stderr on exit 0 is never injected into model context by any hook event, so #1545's wiring fired without reaching the model. The advisory is now delivered via `hookSpecificOutput.additionalContext` (JSON stdout, exit 0) — the confirmed non-blocking context-injection path for both events; stderr is retained only as a human-visible audit trail. No top-level `decision`/`continue`/`stopReason` field is emitted, so the hook stays advisory (never blocks a Stop/SubagentStop). |
|
|
18
18
|
| Prompt-based | CLAUDE.md + rules/ + PostCompact | All MUST rules | Behavioral guidance in context |
|
|
19
19
|
|
|
20
20
|
<!--
|
|
@@ -34,7 +34,7 @@ oh-my-customcode uses an **advisory-first enforcement model**. Most rules are en
|
|
|
34
34
|
3. **Composability**: External skills and internal rules can coexist without deadlocks
|
|
35
35
|
4. **PostCompact reinforcement**: R007/R008/R009/R010/R018 are re-injected after context compaction
|
|
36
36
|
|
|
37
|
-
## Hard Enforcement Candidates — R010 git-delegation-guard (conditional), R007/R008
|
|
37
|
+
## Hard Enforcement Candidates — R010 git-delegation-guard (conditional), R007/R008 advisory **implemented** (#1229 UserPromptSubmit, proactive) + **#1545 SubagentStop** (closes autonomous-loop gap) + retroactive Stop-hook (#1190); model-context delivery fixed via `hookSpecificOutput.additionalContext` (#1547, v1.1.40); hard-block variant still candidate if advisory insufficient (#1096). Promoted: rule-deletion-guard.sh (2026-04-08). See details via Read tool.
|
|
38
38
|
|
|
39
39
|
<!-- DETAIL: Hard Enforcement Candidates (Future)
|
|
40
40
|
If advisory enforcement proves insufficient for specific rules, these are candidates for promotion to hard-block:
|
|
@@ -42,7 +42,7 @@ If advisory enforcement proves insufficient for specific rules, these are candid
|
|
|
42
42
|
| Rule | Candidate Hook | Status | Condition for Promotion |
|
|
43
43
|
|------|---------------|--------|------------------------|
|
|
44
44
|
| R010 | git-delegation-guard.sh | Candidate | If orchestrator-direct-write violations exceed 3/session |
|
|
45
|
-
| R007/R008 | `r007-r008-drift-advisor.sh` (UserPromptSubmit
|
|
45
|
+
| R007/R008 | `r007-r008-drift-advisor.sh` (UserPromptSubmit #1229 + SubagentStop #1545) | **Advisory implemented** — proactive pre-response check now wired to both UserPromptSubmit and SubagentStop; the SubagentStop leg (#1545) closes the no-user-input autonomous-loop gap (`/fsd` etc.). Retroactive: `session-reflection.sh` (Stop, #1190). Two-layer drift detection: proactive (#1229/#1545) + retroactive (#1190). **Delivery mechanism fixed (#1547, v1.1.40)** — stderr on exit 0 reaches only a human (transcript debug mode), never the model, so wiring alone did not prove advisory reach; the advisory now travels via `hookSpecificOutput.additionalContext` on JSON stdout with exit 0, and emits no top-level `decision`/`continue`/`stopReason` so it remains non-blocking. | Promote to hard-block if advisory proves insufficient (#1096) |
|
|
46
46
|
|
|
47
47
|
Promotion requires: (1) measured violation rate data, (2) user approval, (3) rollback plan.
|
|
48
48
|
|
package/templates/manifest.json
CHANGED