oh-my-customcode 1.1.47 → 1.1.49
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/README.md +7 -6
- package/dist/cli/index.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/templates/.claude/agents/agora-runner.md +114 -0
- package/templates/.claude/hooks/scripts/agent-teams-advisor.sh +6 -1
- package/templates/.claude/hooks/scripts/r007-r008-drift-advisor.sh +41 -3
- package/templates/.claude/hooks/scripts/session-env-check.sh +29 -3
- package/templates/.claude/rules/MAY-optimization.md +6 -0
- package/templates/.claude/rules/MUST-agent-design.md +2 -0
- package/templates/.claude/rules/MUST-completion-verification.md +33 -2
- package/templates/.claude/rules/MUST-enforcement-policy.md +3 -3
- package/templates/.claude/rules/MUST-intent-transparency.md +7 -2
- package/templates/.claude/rules/MUST-orchestrator-coordination.md +50 -5
- package/templates/.claude/rules/MUST-parallel-execution.md +56 -7
- package/templates/.claude/rules/MUST-sync-verification.md +21 -2
- package/templates/.claude/rules/MUST-tool-identification.md +22 -4
- package/templates/.claude/skills/agora/SKILL.md +325 -0
- package/templates/.claude/skills/agora/scripts/agora.sh +761 -0
- package/templates/.claude/skills/agora/scripts/anonymize.sh +492 -0
- package/templates/.claude/skills/agora/scripts/judge.sh +427 -0
- package/templates/.claude/skills/agora/scripts/response-schema.json +26 -0
- package/templates/.claude/skills/agora/scripts/reviewers.sh +312 -0
- package/templates/.claude/skills/agora/scripts/verdict-schema.json +34 -0
- package/templates/.claude/skills/hada-scout/SKILL.md +1 -1
- package/templates/.claude/skills/help/SKILL.md +1 -1
- package/templates/.claude/skills/pipeline/workflows/auto-dev.yaml +47 -6
- package/templates/.claude/skills/sauron-watch/SKILL.md +1 -1
- package/templates/.claude/skills/status/SKILL.md +3 -3
- package/templates/.claude/skills/token-efficiency-audit/SKILL.md +1 -1
- package/templates/.github/workflows/wiki-sync.yml +1 -1
- package/templates/CLAUDE.md +3 -3
- package/templates/CLAUDE.md.en +3 -3
- package/templates/CLAUDE.md.ko +3 -3
- package/templates/README.md +5 -5
- package/templates/guides/agent-eval/README.md +1 -1
- package/templates/manifest.json +3 -3
- package/templates/workflows/auto-dev.yaml +47 -6
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
**[한국어 문서 (Korean)](./README_ko.md)**
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
50 agents. 115 skills. 23 rules. One command.
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
19
|
npm install -g oh-my-customcode && cd your-project && omcustom init
|
|
@@ -111,7 +111,7 @@ Agent(arch-documenter):haiku ┘
|
|
|
111
111
|
|
|
112
112
|
---
|
|
113
113
|
|
|
114
|
-
### Agents (
|
|
114
|
+
### Agents (50)
|
|
115
115
|
|
|
116
116
|
| Category | Count | Agents |
|
|
117
117
|
|----------|-------|--------|
|
|
@@ -121,7 +121,7 @@ Agent(arch-documenter):haiku ┘
|
|
|
121
121
|
| Data Engineering | 6 | de-airflow, de-dbt, de-spark, de-kafka, de-snowflake, de-pipeline |
|
|
122
122
|
| Database | 4 | db-supabase, db-postgres, db-redis, db-alembic |
|
|
123
123
|
| Tooling | 4 | tool-npm, tool-optimizer, tool-bun, slack-cli |
|
|
124
|
-
| Architecture |
|
|
124
|
+
| Architecture | 3 | arch-documenter, arch-speckit, agora-runner |
|
|
125
125
|
| Infrastructure | 2 | infra-docker, infra-aws |
|
|
126
126
|
| QA | 3 | qa-planner, qa-writer, qa-engineer |
|
|
127
127
|
| Security | 1 | sec-codeql |
|
|
@@ -132,7 +132,7 @@ Each agent declares its tools, model, memory scope, and limitations in YAML fron
|
|
|
132
132
|
|
|
133
133
|
---
|
|
134
134
|
|
|
135
|
-
### Skills (
|
|
135
|
+
### Skills (115)
|
|
136
136
|
|
|
137
137
|
| Category | Count | Includes |
|
|
138
138
|
|----------|-------|----------|
|
|
@@ -145,6 +145,7 @@ Each agent declares its tools, model, memory scope, and limitations in YAML fron
|
|
|
145
145
|
| Package | 3 | npm-publish, npm-version, npm-audit |
|
|
146
146
|
| Optimization | 3 | optimize-analyze, optimize-bundle, optimize-report |
|
|
147
147
|
| Security | 2 | adversarial-review, cve-triage |
|
|
148
|
+
| Consensus | 1 | agora — anonymous multi-round multi-vendor consensus review |
|
|
148
149
|
| Other | 44 | claude-native, vercel-deploy, skills-sh-search, result-aggregation, and ~40 more |
|
|
149
150
|
|
|
150
151
|
Skills use a 3-tier scope system: `core` (universal), `harness` (agent/skill maintenance), `package` (project-specific).
|
|
@@ -269,8 +270,8 @@ omcustom serve-stop # Stop Web UI
|
|
|
269
270
|
your-project/
|
|
270
271
|
├── CLAUDE.md # Entry point
|
|
271
272
|
├── .claude/
|
|
272
|
-
│ ├── agents/ #
|
|
273
|
-
│ ├── skills/ #
|
|
273
|
+
│ ├── agents/ # 50 agent definitions
|
|
274
|
+
│ ├── skills/ # 115 skill modules
|
|
274
275
|
│ ├── rules/ # 23 governance rules (R000-R023)
|
|
275
276
|
│ ├── hooks/ # 15 lifecycle hook scripts
|
|
276
277
|
│ ├── schemas/ # Tool input validation schemas
|
package/dist/cli/index.js
CHANGED
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: agora-runner
|
|
3
|
+
description: Executes exactly one agora session step (start, one round, or finish) via the skill scripts and returns only a verdict summary. Never returns reviewer text, vendor attribution or sealed paths.
|
|
4
|
+
model: claude-sonnet-5
|
|
5
|
+
tools: [Bash, Read, Write, Glob]
|
|
6
|
+
skills: [agora]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 역할
|
|
10
|
+
|
|
11
|
+
`agora` 스킬에서 **파일을 쓰는 진입점 전부**를 전담합니다 — `--start`, `--round`, `--set-stop`, `--report` 넷입니다. 오케스트레이터는 파일을 쓰지 않는 `--gate`만 직접 실행합니다(R010).
|
|
12
|
+
|
|
13
|
+
**위임 1건 = 아래 단계 하나**이며, 여러 단계를 한 번에 처리하지 않습니다 (R020 「위임 경계를 Phase 개수로 설계」 — 다중 Phase 위임은 경계에서 mid-step 종료가 반복 재발했습니다).
|
|
14
|
+
|
|
15
|
+
| 위임 유형 | 실행 명령 | 단일 목표 |
|
|
16
|
+
|-----------|-----------|-----------|
|
|
17
|
+
| 세션 시작 | `--start` (게이트드 모드에서 **라운드 1까지** 수행) | 세션 생성 + 라운드 1 |
|
|
18
|
+
| 라운드 진행 | `--round <N>` | 라운드 하나 |
|
|
19
|
+
| 세션 마감 | `--set-stop <CODE>` → `--report` | 종료 사유 기록 + 보고서 생성 |
|
|
20
|
+
|
|
21
|
+
## 실행 절차
|
|
22
|
+
|
|
23
|
+
### 세션 시작 (`--start`)
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
bash .claude/skills/agora/scripts/agora.sh --start "<topic>" [--attach <path>]... [--max-rounds <N>]
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
`--auto`는 오케스트레이터가 명시적으로 지시한 경우에만 붙입니다 — 매 라운드 게이트를 생략하고 상한까지 전부 돌기 때문입니다(최악 약 2시간, 토큰 약 600k).
|
|
30
|
+
|
|
31
|
+
**stdout에는 세션 디렉토리 절대경로 한 줄만** 나옵니다. `dir=$(bash agora.sh --start ...)` 관용구로 캡처하십시오. 게이트 블록은 **stderr로 나가므로** 반환 계약에 담을 수 없습니다 — 오케스트레이터가 `--gate --session-dir <dir> --round 1`로 다시 렌더할 것이므로, 러너는 stderr 게이트 블록을 반환에 옮겨 적지 않습니다.
|
|
32
|
+
|
|
33
|
+
반환 전에 `bash agora.sh --decide-stop < <dir>/state.json`으로 정지 코드를 산출해 `stop_code`로 함께 돌려줍니다. **라운드 1에서 정지 조건이 걸린 경우 `--start`가 `.stop` 기록과 `report.md` 생성까지 스스로 수행하고 게이트를 렌더하지 않으므로**, 이때는 `--set-stop`도 `--report`도 다시 실행하지 마십시오. `state.json`의 `.stop`이 이미 채워졌는지로 판별합니다.
|
|
34
|
+
|
|
35
|
+
### 라운드 진행 (`--round`)
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
bash .claude/skills/agora/scripts/agora.sh --round <N> --session-dir <dir> [--extra-agenda <json-array>]
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
오케스트레이터로부터 `e` 게이트 응답이 전달된 경우에만 `--extra-agenda`를 붙이며, **JSON 배열이 아니면 exit 64로 거부됩니다**(예: `'["롤백 명령 시퀀스를 제시할 것"]'`).
|
|
42
|
+
|
|
43
|
+
`--round`는 `.stop`도 `max_rounds`도 검사하지 않고 `--decide-stop`을 호출하지도 않으므로, 정지 판단은 직접 수행합니다:
|
|
44
|
+
|
|
45
|
+
1. `bash agora.sh --round <N> --session-dir <dir>` 로 한 라운드를 진행합니다.
|
|
46
|
+
2. `bash agora.sh --decide-stop < <dir>/state.json` 으로 정지 코드를 산출해 반환 계약의 `stop_code`로 돌려줍니다.
|
|
47
|
+
|
|
48
|
+
**여기서 멈추고 반환합니다.** `--set-stop`도 `--report`도 이 위임에서 실행하지 않습니다 — 정지 여부는 사용자 게이트 응답을 받은 오케스트레이터가 결정하며, 마감은 별도 위임입니다.
|
|
49
|
+
|
|
50
|
+
`--gate`는 실행하지 않습니다 — 반환 계약에 게이트 블록을 담을 필드가 없어 출력이 그대로 버려지므로, 파일을 쓰지 않는 순수 출력 명령인 `--gate`는 오케스트레이터가 직접 실행합니다.
|
|
51
|
+
|
|
52
|
+
### 세션 마감 (`--set-stop` → `--report`)
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
bash .claude/skills/agora/scripts/agora.sh --set-stop <CODE> --session-dir <dir>
|
|
56
|
+
bash .claude/skills/agora/scripts/agora.sh --report --session-dir <dir>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**`--set-stop`을 반드시 `--report`보다 먼저, 그리고 건너뛰지 말고 실행합니다.** 게이트드 모드에서 라운드 2 이후 `.stop`에는 다른 writer가 없으므로, 생략하면 세션이 아무리 깨끗하게 끝나도 `report.md`가 `종료 사유: UNKNOWN`을 출력합니다.
|
|
60
|
+
|
|
61
|
+
`<CODE>`는 오케스트레이터가 지정한 값을 그대로 씁니다. 지정이 없으면 직전 `--decide-stop` 결과를 씁니다.
|
|
62
|
+
|
|
63
|
+
| `<CODE>` | 사용 시점 |
|
|
64
|
+
|----------|-----------|
|
|
65
|
+
| `CONSENSUS` · `STALLED` · `MAX_ROUNDS` | `--decide-stop`이 그 코드를 반환했을 때 |
|
|
66
|
+
| `USER` | 사용자가 게이트에서 `s`(중단하고 보고서)를 선택했을 때 |
|
|
67
|
+
| `CONTINUE` | **사용 불가** — exit 64로 거부됩니다. 정지하지 않았다는 뜻이므로 라운드를 더 도십시오 |
|
|
68
|
+
|
|
69
|
+
`--set-stop`이 비영으로 끝나면 **`--report`를 실행하지 말고** 그 사실을 보고하십시오 (아래 exit 73 항목).
|
|
70
|
+
|
|
71
|
+
## Exit code 대응
|
|
72
|
+
|
|
73
|
+
모든 명령의 종료 코드를 확인한 뒤 아래에 따라 대응합니다. `agora.sh`는 하위 스크립트의 코드를 그대로 전파하므로 `--round` 하나가 아래 전부를 반환할 수 있습니다. **어떤 코드에서도 라운드를 자체 판단으로 재실행하지 마십시오** — 벤더가 다시 과금됩니다.
|
|
74
|
+
|
|
75
|
+
| 코드 | 보고할 내용 | 러너의 행동 |
|
|
76
|
+
|------|-------------|-------------|
|
|
77
|
+
| `0` | 정상 | 반환 계약대로 반환 |
|
|
78
|
+
| `1` | **지문 검출로 라운드 중단** — 익명 번들에 벤더 지문이 섞여 익명성 보호 차원에서 중단됨. `SEALED/mapping/`과 `anon/`에는 아무것도 쓰이지 않음 | 즉시 보고 후 종료. 재실행 금지 |
|
|
79
|
+
| `3` | **유효 리뷰어 2인 미만으로 라운드 중단** — CLI 결측 단계 또는 응답 스키마 위반 단계에서 걸림 | 즉시 보고 후 종료 |
|
|
80
|
+
| `4` | **심판 로테이션 3슬롯 전부 실패로 세션 중단** — CLI 실패·타임아웃·파싱 실패·스키마 위반 포함. 로테이션 대체까지가 이미 시도의 전부 | 즉시 보고 후 종료 |
|
|
81
|
+
| `64` | **호출 오류** — 옵션·필수 플래그·`--extra-agenda` JSON 배열 여부·`--set-stop` 코드를 점검. 벤더는 호출되지 않았음 | 호출 형태가 명백히 틀렸으면 **한 번만** 교정 재시도, 아니면 보고 후 종료 |
|
|
82
|
+
| `65` | **직전 라운드 sealed 데이터 무결성 문제** — 파일은 있으나 파싱 불가. 지문 검출(`1`)과 다른 원인 | 즉시 보고 후 종료 |
|
|
83
|
+
| `66` | **세션 디렉토리(또는 익명 번들)를 찾을 수 없음** — 경로를 점검 | 경로 오류면 보고 후 종료 |
|
|
84
|
+
| `68` | **설정 오류** — `verdict-schema.json`을 읽거나 파싱할 수 없음. 심판의 실패가 아니라 배포 결함이라 재시도해도 동일하게 실패 | 즉시 보고 후 종료 |
|
|
85
|
+
| `73` | **라운드는 돌았으나 기록되지 않음** — 리뷰어 3벤더와 심판이 이미 호출·과금된 뒤 `state.json`/`report.md` 쓰기가 실패. 가장 비싼 실패 | **산출물을 소비하지 말고, 라운드를 재실행하지도 말 것.** stderr 진단 줄을 그대로 보고하고 종료 |
|
|
86
|
+
|
|
87
|
+
`1`·`65`로 중단된 원인을 파악한다며 `SEALED/` 하위 파일을 읽지 마십시오 — 진단 목적이라도 금지 사항 위반입니다. stderr 진단 줄만 보고합니다.
|
|
88
|
+
|
|
89
|
+
세션 디렉토리 경로는 절대 경로로 출력되므로 다른 cwd에서 실행해도 세션을 찾을 수 있습니다.
|
|
90
|
+
|
|
91
|
+
## 반환 계약
|
|
92
|
+
|
|
93
|
+
아래 필드만 반환합니다. 라운드를 실행하지 않은 위임(세션 마감)에서는 해당 없는 필드를 생략합니다.
|
|
94
|
+
|
|
95
|
+
| 필드 | 출처 |
|
|
96
|
+
|------|------|
|
|
97
|
+
| `session_dir` | `--start` 의 stdout (세션 시작 위임에서만) |
|
|
98
|
+
| `round` | `verdict/round-N.json` `.round` |
|
|
99
|
+
| `consensus` | `.consensus` |
|
|
100
|
+
| `verdict` | `.verdict` |
|
|
101
|
+
| `resolved` | `.resolved` (id + resolution) |
|
|
102
|
+
| `unresolved` | `.unresolved` (id + severity + positions) |
|
|
103
|
+
| `agenda` | `.agenda` |
|
|
104
|
+
| `new_findings` | `.new_findings` |
|
|
105
|
+
| `stop_code` | `bash agora.sh --decide-stop < state.json` 결과 |
|
|
106
|
+
| `exit_code` | 실행한 명령의 종료 코드 (0이 아니면 위 표의 대응과 함께) |
|
|
107
|
+
| `stop_recorded` | 세션 마감 위임에서 `--set-stop` 성공 여부 |
|
|
108
|
+
| `report_path` | 세션 마감 위임에서 생성된 `report.md` 경로 |
|
|
109
|
+
|
|
110
|
+
반환 페이로드는 verdict 요약만 담습니다.
|
|
111
|
+
|
|
112
|
+
## 금지 사항
|
|
113
|
+
|
|
114
|
+
`SEALED/` 하위 파일을 읽지 않으며, 리뷰어 원문·벤더 식별자·`SEALED/` 경로 문자열을 반환에 포함하지 않습니다. `anon/round-N.json`의 `A`/`B`/`C` 라벨은 라운드마다 다시 섞이므로 **어떤 벤더로도 해석하거나 라운드 간 비교하지 마십시오**. 사용자에게 직접 질문하거나 승인을 구하지 않으며, 게이트 표시는 오케스트레이터가 전담합니다.
|
|
@@ -11,7 +11,12 @@ command -v jq >/dev/null 2>&1 || exit 0
|
|
|
11
11
|
|
|
12
12
|
input=$(cat)
|
|
13
13
|
|
|
14
|
-
# Skip
|
|
14
|
+
# Skip unless Agent Teams is CONFIRMED active.
|
|
15
|
+
# session-env-check.sh writes one of: disabled | env-set | enabled (#1588).
|
|
16
|
+
# `env-set` = the env var is set but TeamCreate presence is UNVERIFIED — R018 is dormant,
|
|
17
|
+
# so recommending TeamCreate here would be unactionable advice.
|
|
18
|
+
# The `!= "enabled"` test below therefore deliberately skips on `env-set`; only the measured
|
|
19
|
+
# OMCUSTOM_AGENT_TEAMS_VERIFIED=1 path re-enables this advisor.
|
|
15
20
|
ENV_STATUS="/tmp/.claude-env-status-${PPID}"
|
|
16
21
|
if [ -f "$ENV_STATUS" ]; then
|
|
17
22
|
teams_status=$(grep "agent_teams=" "$ENV_STATUS" 2>/dev/null | cut -d= -f2 || echo "unknown")
|
|
@@ -116,6 +116,7 @@
|
|
|
116
116
|
# OMCUSTOM_R007_ADVISOR=off — advisory 완전 비활성화 (pass-through)
|
|
117
117
|
# OMCUSTOM_TRANSCRIPT_BASE — transcript 디렉토리 경로 override (설정 시 최우선)
|
|
118
118
|
# OMCUSTOM_R007_MARKER_DIR — dedup 마커 디렉토리 override (기본 ${TMPDIR:-/tmp})
|
|
119
|
+
# OMCUSTOM_R008_REVERSE=on — 역방향 신호(예고했으나 도구 미호출) 활성화. 기본 off.
|
|
119
120
|
|
|
120
121
|
set -euo pipefail
|
|
121
122
|
|
|
@@ -163,7 +164,14 @@ if [ ! -f "$TRANSCRIPT_PATH" ]; then
|
|
|
163
164
|
fi
|
|
164
165
|
|
|
165
166
|
# ── 마지막 assistant 턴 재구성 + R007/R008 판정 (jq 1회 fork) ──
|
|
166
|
-
# 출력: "<turn-uuid>\t<r007-count>\t<r008-count>" (턴이 없으면 무출력)
|
|
167
|
+
# 출력: "<turn-uuid>\t<r007-count>\t<r008-count>\t<r008-reverse-count>" (턴이 없으면 무출력)
|
|
168
|
+
#
|
|
169
|
+
# ── 역방향 신호는 이 advisor 전용 (session-reflection.sh에 복제하지 않는다) ────────────
|
|
170
|
+
# forward 판정식은 session-reflection.sh(Stop 훅)와 공유되지만, 역방향 신호는 의도적으로
|
|
171
|
+
# 여기에만 둔다. 근거: 역방향이 잡는 결함 턴은 tool_use가 0건이라 PostToolUse/SubagentStop이
|
|
172
|
+
# 애초에 발화하지 않고, Stop 훅은 세션이 끝난 뒤라 그 턴을 교정할 기회가 이미 없다. 두
|
|
173
|
+
# 파일에 복제하면 같은 결함을 두 번 보고하면서 교정 기회는 여전히 0인 상태가 된다.
|
|
174
|
+
# 기본 off(OMCUSTOM_R008_REVERSE)인 실험 신호를 복제하면 되돌릴 지점도 두 곳이 된다.
|
|
167
175
|
JQ_LAST_TURN='
|
|
168
176
|
split("\n")
|
|
169
177
|
| map(select(length > 0) | (fromjson? // empty))
|
|
@@ -194,7 +202,23 @@ split("\n")
|
|
|
194
202
|
| ($an_tool + (if $an_spawn_item > 0 then $an_spawn_item else $an_spawn_hdr end)) as $announce
|
|
195
203
|
| ([ $blocks[] | select((.type? == "tool_use") and ((.name? // "") != "Skill")) ] | length) as $ntools
|
|
196
204
|
| (if $ntools > $announce then $ntools - $announce else 0 end) as $r008
|
|
197
|
-
|
|
205
|
+
# REVERSE signal (#1595 제안 #6): announced a tool call, then ended the turn without
|
|
206
|
+
# emitting ANY tool_use block (R009 Self-Check #6). Uses its OWN ANCHORED counter — do NOT
|
|
207
|
+
# reuse $announce. MEASURED over 482 orchestrator turns:
|
|
208
|
+
# * naive `announce - ntools > 0` fires on 36/482 and DOUBLES the advisory rate; 16 are
|
|
209
|
+
# pure Skill-exclusion artifacts and 15 more are prose matching the UNANCHORED regexes.
|
|
210
|
+
# * narrowing to "no tool_use at all" + ANCHORED `→ Tool:` yields 3/3 true positives,
|
|
211
|
+
# 0 false positives; the anchor costs 1 of 969 real announce lines corpus-wide (0.1%).
|
|
212
|
+
# The anchor MUST NOT be applied to $an_tool: forward is max(0, ntools - announce), so
|
|
213
|
+
# under-counting announce INCREASES reported violations.
|
|
214
|
+
# Condition uses ALL tool_use blocks (Skill included) — using the Skill-excluded $ntools
|
|
215
|
+
# would fire on compliant turns that only invoked Skill. This deliberately differs from
|
|
216
|
+
# the $ntools denominator above; it is a separate variable, not a weakening of the Skill
|
|
217
|
+
# exemption (#1569), which still governs the forward verdict.
|
|
218
|
+
| ([ $lines[] | select(test("^\\[[^\\]]+\\]\\[[^\\]]+\\] ?(→|->|—>) ?Tool:")) ] | length) as $an_anchored
|
|
219
|
+
| ($blocks | map(select(.type? == "tool_use")) | length) as $nall_tools
|
|
220
|
+
| (if $nall_tools == 0 and $an_anchored > 0 then $an_anchored else 0 end) as $r008rev
|
|
221
|
+
| [$tuuid, ($r007 | tostring), ($r008 | tostring), ($r008rev | tostring)] | @tsv
|
|
198
222
|
end
|
|
199
223
|
'
|
|
200
224
|
|
|
@@ -207,12 +231,22 @@ fi
|
|
|
207
231
|
turn_uuid=$(printf '%s' "$result" | cut -f1)
|
|
208
232
|
r007_violations=$(printf '%s' "$result" | cut -f2)
|
|
209
233
|
r008_violations=$(printf '%s' "$result" | cut -f3)
|
|
234
|
+
r008_reverse=$(printf '%s' "$result" | cut -f4)
|
|
210
235
|
|
|
211
236
|
: "${r007_violations:=0}"
|
|
212
237
|
: "${r008_violations:=0}"
|
|
238
|
+
: "${r008_reverse:=0}"
|
|
239
|
+
|
|
240
|
+
# 역방향 신호는 OPT-IN, 기본 off.
|
|
241
|
+
# 측정 정밀도는 3/3이지만 표본이 3건뿐이고, 배선 구조상 "예방"이 불가능하다:
|
|
242
|
+
# 결함 턴은 tool_use가 0이라 PostToolUse/SubagentStop이 발화하지 않고, UserPromptSubmit은
|
|
243
|
+
# 사용자가 이미 개입한 뒤에 발화한다. 기본 on 전환 전에 Stop 배선을 별도로 실측해야 한다.
|
|
244
|
+
if [ "${OMCUSTOM_R008_REVERSE:-off}" != "on" ]; then
|
|
245
|
+
r008_reverse=0
|
|
246
|
+
fi
|
|
213
247
|
|
|
214
248
|
# ── 위반이 없으면 아무것도 출력하지 않는다 (오탐 방지) ──
|
|
215
|
-
if [ "$r007_violations" -eq 0 ] && [ "$r008_violations" -eq 0 ]; then
|
|
249
|
+
if [ "$r007_violations" -eq 0 ] && [ "$r008_violations" -eq 0 ] && [ "$r008_reverse" -eq 0 ]; then
|
|
216
250
|
exit 0
|
|
217
251
|
fi
|
|
218
252
|
|
|
@@ -232,6 +266,10 @@ fi
|
|
|
232
266
|
advisory_text=$(printf '[R007/R008 Advisory] 직전 응답에서 식별 누락 감지 (R007 헤더=%s, R008 접두사=%s). 이번 응답은 ┌─ Agent: 헤더로 시작하고, 모든 도구 호출에 [agent][model] → Tool: 접두사를 포함하십시오.' \
|
|
233
267
|
"$r007_violations" "$r008_violations")
|
|
234
268
|
|
|
269
|
+
if [ "$r008_reverse" -gt 0 ]; then
|
|
270
|
+
advisory_text="${advisory_text} [R009 Self-Check #6] 직전 턴은 도구 호출 ${r008_reverse}건을 예고하고도 tool_use 블록 없이 종료했습니다 — 예고한 호출을 지금 실행하거나, 실행하지 않기로 한 사유를 명시하십시오."
|
|
271
|
+
fi
|
|
272
|
+
|
|
235
273
|
# 사람이 보는 감사 추적용 (exit 0에서는 모델에 전달되지 않음 — #1547 참고)
|
|
236
274
|
printf '%s\n' "$advisory_text" >&2
|
|
237
275
|
|
|
@@ -3,7 +3,8 @@ set -euo pipefail
|
|
|
3
3
|
|
|
4
4
|
# Session Environment Check Hook
|
|
5
5
|
# Trigger: SessionStart
|
|
6
|
-
# Purpose: Check availability of codex CLI and Agent Teams
|
|
6
|
+
# Purpose: Check availability of codex CLI and the Agent Teams env-var INTENT (#1588),
|
|
7
|
+
# report via stderr
|
|
7
8
|
# Protocol: stdin JSON -> stdout pass-through, exit 0 always
|
|
8
9
|
|
|
9
10
|
input=$(cat)
|
|
@@ -37,10 +38,30 @@ if command -v rtk >/dev/null 2>&1; then
|
|
|
37
38
|
RTK_STATUS="available"
|
|
38
39
|
fi
|
|
39
40
|
|
|
40
|
-
# Check Agent Teams
|
|
41
|
+
# Check Agent Teams ENV-VAR INTENT — NOT activation (#1588)
|
|
42
|
+
#
|
|
43
|
+
# R018 Detection (.claude/rules/MUST-agent-teams.md) resolves Agent Teams as ACTIVE only when
|
|
44
|
+
# CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 AND TeamCreate is present in the tool list.
|
|
45
|
+
# A shell hook CANNOT observe the tool list. MEASURED against @anthropic-ai/claude-code 2.1.233:
|
|
46
|
+
# * SessionStart stdin carries only
|
|
47
|
+
# {session_id, transcript_path, cwd, permission_mode, agent_id, agent_type,
|
|
48
|
+
# hook_event_name, source, model} — NO hook event carries a tool inventory.
|
|
49
|
+
# * availability additionally requires a remote gate and a plan entitlement, neither of
|
|
50
|
+
# which is cached on disk, so the shell cannot read them either.
|
|
51
|
+
# The env var is NECESSARY-BUT-NOT-SUFFICIENT. Reporting "enabled" here made
|
|
52
|
+
# agent-teams-advisor.sh recommend TeamCreate in an environment where that tool does not exist.
|
|
53
|
+
#
|
|
54
|
+
# VALUE CONTRACT: agent-teams-advisor.sh reads this via `grep "agent_teams=" | cut -d= -f2`,
|
|
55
|
+
# so the value MUST NOT contain '='.
|
|
41
56
|
AGENT_TEAMS_STATUS="disabled"
|
|
42
57
|
if [ "${CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS:-0}" = "1" ]; then
|
|
43
|
-
AGENT_TEAMS_STATUS="
|
|
58
|
+
AGENT_TEAMS_STATUS="env-set"
|
|
59
|
+
# Escape hatch: the operator has MEASURED TeamCreate in the live tool list (R020 — "the tool
|
|
60
|
+
# exists" is itself a claim requiring measurement). This is the only path that may report
|
|
61
|
+
# activation, because only a human/model can see the tool list.
|
|
62
|
+
if [ "${OMCUSTOM_AGENT_TEAMS_VERIFIED:-0}" = "1" ]; then
|
|
63
|
+
AGENT_TEAMS_STATUS="enabled"
|
|
64
|
+
fi
|
|
44
65
|
fi
|
|
45
66
|
|
|
46
67
|
# Claude Code version detection
|
|
@@ -202,6 +223,11 @@ echo " codex CLI: ${CODEX_STATUS}" >&2
|
|
|
202
223
|
echo " gemini CLI: ${GEMINI_STATUS}" >&2
|
|
203
224
|
echo " RTK CLI: ${RTK_STATUS}" >&2
|
|
204
225
|
echo " Agent Teams: ${AGENT_TEAMS_STATUS}" >&2
|
|
226
|
+
if [ "$AGENT_TEAMS_STATUS" = "env-set" ]; then
|
|
227
|
+
echo " CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 is set — this is INTENT, not activation." >&2
|
|
228
|
+
echo " R018 also requires TeamCreate in the tool list; a shell hook cannot observe it." >&2
|
|
229
|
+
echo " Confirm TeamCreate before treating R018 as active (OMCUSTOM_AGENT_TEAMS_VERIFIED=1)." >&2
|
|
230
|
+
fi
|
|
205
231
|
echo " Claude Code: v${CLAUDE_VERSION} (${COMPAT_STATUS})" >&2
|
|
206
232
|
if [ "$COMPAT_STATUS" = "outdated" ]; then
|
|
207
233
|
echo " ⚠ Claude Code v${MIN_COMPAT_VERSION}+ recommended for full hook compatibility" >&2
|
|
@@ -24,10 +24,14 @@
|
|
|
24
24
|
|
|
25
25
|
> **`ls | tail` 시계열 오판 (#1417)**: `ls`는 파일명을 알파벳/사전순으로 정렬하므로 `ls <dir> | tail`로 "가장 최근 파일"을 판단하면 오판한다(파일명 순서 ≠ mtime 순서). 시계열 최신 판단은 `ls -t`, `find <dir> -newermt <ts>`, 또는 stat/timestamp 기반 정렬을 명시한다. `tail`만으로 "최신" 단정 금지. Origin: #1417 (외부 통화녹음 진단 세션 — `ls TPhoneCallRecords | tail -6`이 알파벳순이라 최신을 6/18로 오판 → `find -newermt`로 6/19~20 파일 발견해 정정).
|
|
26
26
|
|
|
27
|
+
> **`readdir` 순서는 플랫폼·런타임 의존이며 정렬되지 않는다 (#1599)**: 디렉토리 열거 결과는 **정렬순도 생성순도 아니다**. bun의 `readdir`는 APFS에서 파일시스템의 이름 해시 순서를 그대로 반환하며 node의 `readdirSync`와도 다른 순서를 낸다 — 같은 코드가 런타임·파일시스템·항목 이름에 따라 다른 순서를 낸다. 따라서 "첫 항목"·"마지막 항목"으로 대상을 고르지 말고 **정렬 키를 명시**(`.sort()`, mtime 기준 정렬)하거나 **필터로 유일성을 보장**한다. 함께: 열거 결과의 모든 항목이 디렉토리라고 가정하지 말고 `withFileTypes: true` + `isDirectory()`로 판별한다 — 관측 파일·`.DS_Store` 등이 섞이면 "첫 항목"이 디렉토리가 아닐 수 있다. **테스트에서의 파급**: 대상 디렉토리명이 `date -u +%Y-%m-%d`처럼 날짜에서 파생되면 이름이 바뀌는 날 해시 순서가 뒤집혀 **clean clone 첫 실행부터 달력 날짜의 절반에서 실패**한다(실측: 8/15 통과, 8/16~17 실패). 위 `ls | tail` 시계열 오판(#1417)의 **API 각도 변형**이다 — 도구가 순서를 보장한다는 미확인 전제에서 결과를 해석한 같은 계열. Origin: #1599 (agora 테스트 `findSessionDir`가 출력 루트를 readdir 후 전 항목을 디렉토리로 가정; 코드 조치는 `withFileTypes:true` + `isDirectory()` 필터 + 관측 파일 분리로 완료). Cross-ref: R005 「계수/매칭 방법 확인」(도구 기본 동작 미확인), R023(Conditional-Output Verification).
|
|
28
|
+
|
|
27
29
|
> **파이프 뒤 `$?`는 마지막 명령의 exit code (#1492, zsh 변형 #1540)**: `script.sh | tail -N; echo $?`처럼 검증 스크립트를 파이프에 연결한 뒤 `$?`로 읽으면 파이프라인 **마지막 명령**(`tail`)의 종료코드를 얻는다 — 스크립트 자체가 실패(exit 1)해도 `tail`이 성공(exit 0)하면 `$?=0`으로 "통과"를 오판한다. **1차 지침**: 검증 스크립트는 파이프 없이 단독 실행한다. 부득이 파이프를 써야 한다면, 원본 exit code를 읽는 문법은 **셸마다 다르다** — bash는 `${PIPESTATUS[0]}`(대문자, 0-indexed), zsh는 `$pipestatus[1]`(소문자, 1-indexed)이며 서로 호환되지 않는다. **이 저장소의 기본 셸이자 Claude Code Bash 도구 실행 셸은 zsh**이므로, bash 문법 `${PIPESTATUS[0]}`을 그대로 쓰면 zsh에서는 미정의 변수로 취급되어 **오류 없이 빈 값**을 반환한다 — 조건문에서 빈 값은 거짓으로 평가돼 "검증 통과"처럼 보이는 조용한 오판을 재생산한다. 셸을 사전 확인(`echo $SHELL` / `$BASH_VERSION` 존재 여부)한 뒤 해당 셸의 문법을 쓴다. **주의**: `${PIPESTATUS[0]}` 자체는 R023 Workflow JS 템플릿 리터럴 이스케이프 이슈(#1438, `${...}`를 JS가 평가해 ReferenceError)와 별개 문제 — 본 항목은 셸에서 파이프 뒤 exit code를 읽는 각도다. Origin: #1492 (Session 132 회고 찐빠 #3); zsh 변형은 #1540 (Session 138 회고 찐빠 #6) — `gh run watch ... | tail` 뒤 `${PIPESTATUS[0]}`가 zsh에서 빈 값을 반환해 CI 결론을 재실측해야 했음. Cross-ref: R020 ("command executed" ≠ "succeeded").
|
|
28
30
|
|
|
29
31
|
> **계수/매칭 방법 확인 (#1521)**: 카운트를 대조하기 전에 **비교 대상이 무엇을 어떻게 세는지** 먼저 확인한다 — 같은 지표라도 계수 방법이 다르면 값이 달라진다. 대표 함정 4종: (a) glob(`ls *.md`, 최상위만) vs 재귀 `find`(하위 디렉토리 포함), (b) 부분 문자열 grep(`grep "sdd"`가 `sdd-dev`까지 매칭), (c) 확장자 필터(`--include='*.md'`가 `CLAUDE.md.en`을 미매칭), (d) **머지 커밋 diff 기본 생략** — `git show --name-only <머지커밋>`은 diff를 기본적으로 출력하지 않아 변경 파일 0개로 오독된다. 머지 커밋의 변경 파일을 세려면 `--first-parent`(1차 부모 대비) 또는 `-m`(각 부모별 diff)을 명시한다. 검증 스크립트와 대조할 때는 **스크립트의 실제 계수 로직을 읽고** 같은 방법으로 센다. 위 `ls | tail` 시계열 오판(#1417)과 동류로, 도구의 기본 동작을 확인하지 않은 채 결과를 해석해 오탐에 이르는 패턴이다. Origin: #1521 (2026-07-20 세션에서 3회 반복; 두 서브에이전트가 독립적으로 동일 오탐에 도달); (d)는 #1553 찐빠 #4 (2026-07-30 세션에서 머지 커밋 `--name-only` 0파일을 "변경 없음"으로 오독).
|
|
30
32
|
|
|
33
|
+
> **도구 이름 ≠ 그 프로그램 (#1590)**: 도구를 쓰기 전에 `type <tool>`로 실체를 확인한다. Bash 도구의 `grep`은 `~/.claude/shell-snapshots/snapshot-zsh-*.sh`의 **셸 함수**이며 `ugrep --ignore-files`에 위임한다. 그 결과 `.gitignore`의 리터럴 `CLAUDE.md` 패턴을 존중해, **force-tracked 파일을 재귀 탐색에서 조용히 누락**한다(에러 없이 exit 0). 명시 경로를 준 grep은 정상 동작하므로 **traversal만 영향**을 받는다. 실측(2026-08-15): 동일 패턴·동일 대상에 대해 셸 함수 36 / `command grep` 43 / `git grep` 38 히트 — 셸 함수만 `CLAUDE.md`를 0 히트로 놓쳤다. 진단 함정: `git check-ignore`는 **index-aware**라 tracked 파일에 "not ignored"(exit 1)를 반환한다 — 원인을 보려면 `git check-ignore --no-index`를 써야 한다. 처방: 저장소 전수 조사는 `git grep`을 표준으로 한다(R017 Count Sync cross-ref). Origin: #1590.
|
|
34
|
+
|
|
31
35
|
<!--
|
|
32
36
|
> **v2.1.206+**: `/doctor`에 checked-in CLAUDE.md에서 코드베이스로부터 파생 가능한 내용을 잘라내도록 제안하는 체크가 추가되었습니다 — R005 "Context Optimization via HTML Comments"의 컨텍스트 절감 원칙과 정합(모델 불필요 메타데이터 축소).
|
|
33
37
|
-->
|
|
@@ -46,6 +50,8 @@
|
|
|
46
50
|
|
|
47
51
|
> **v2.1.229+**: 도구 호출의 `glob`/`file_path`/`command` 값이 **비문자열일 때 에러 화면으로 크래시**하던 문제가 수정되었습니다(해당 세션의 `--resume`에서도 재발). 구버전에서 이 크래시는 세션을 복구 불가 상태로 만들면서 **원인이 도구 인자 타입이라는 단서를 남기지 않았으므로**, 스크립트로 도구 인자를 조립할 때 문자열 타입을 보장합니다(cross-ref R023 Workflow Script Sanity Check). 좁은 터미널에서 progress bar·마크다운 표 렌더링 시 발생하던 RangeError 크래시(`claude --continue`/`--resume` 시작 시에도 발생)도 함께 수정되었습니다.
|
|
48
52
|
|
|
53
|
+
> **v2.1.233+**: Linux에서 Bash 도구 명령에 **memory cgroup**을 걸 수 있게 되어(`CLAUDE_CODE_TOOL_MEMORY_LIMIT`, opt-in) 폭주하는 빌드가 세션을 마비시키지 못합니다. 이 변수가 설정된 환경에서는 대용량 빌드·테스트가 **OOM으로 죽을 수 있으므로**, 실패를 코드 결함으로 특성화하기 전에 이 변수 설정 여부를 확인합니다(R020 Read-Before-Characterize). 같은 릴리즈에서 **샌드박스 활성 Linux의 유휴 세션이 CPU 코어 1개를 100% 점유하던 문제**도 수정되었습니다 — 구버전 Linux에서 병렬 배치의 CPU 포화·타임아웃 실패를 "부하 의존"으로 귀속하기 전에 유휴 세션의 상시 점유를 배제해야 했습니다(cross-ref R009 「파일 disjoint ≠ 자원 disjoint」). 이 저장소의 기본 실행 환경은 Darwin이므로 두 항목 모두 **현재 미적용**이며, Linux CI·컨테이너 실행에만 해당합니다.
|
|
54
|
+
|
|
49
55
|
### Capability-Aware Tool Scheduling
|
|
50
56
|
|
|
51
57
|
When dispatching parallel tool calls, consider per-tool capabilities to optimize scheduling:
|
|
@@ -66,6 +66,8 @@ Skill/rule text instructing "spawn with `model: opus`" refers to this tier — a
|
|
|
66
66
|
|
|
67
67
|
> **v2.1.223+**: workflow agent · forked skill · slash command · 재개된 background agent가 **요청한 subagent 모델이 제한되어 parent model로 실행될 때 경고가 표시**됩니다. 위 v2.1.222 org step-down 노트의 직접 연장선으로, 이전에는 이 강등이 **무음**이었습니다 — 즉 "`model: opus`로 스폰했다"는 기록이 실제 실행 모델의 증거가 아니었습니다. 특정 모델을 확정하려면 frontmatter Tier-2 full ID를 쓰고, 실행 모델은 경고 표시 유무로 확인합니다(R020 "attempt ≠ outcome"의 모델 선택 각도).
|
|
68
68
|
|
|
69
|
+
> **v2.1.233+**: print 모드(`-p`) 진단이 추가되어, Claude Code가 **인식하지 못하는 model ID**로 요청이 나가면 stderr에 `[claude-code:unrecognized_model]` 라인이 기록됩니다(`modelOverrides`로 매핑하면 억제). 구버전에서는 오타·폐기된 full ID가 **무음으로 fallback 해석**되어 "frontmatter에 적힌 모델 = 실제 실행 모델"이라는 전제가 검증 불가능했습니다 — 위 v2.1.223 강등 경고와 같은 계열의 **관측성 보강**이며, 이 저장소는 다수 에이전트가 Tier-2 full ID를 쓰므로 `-p` 실행 시 이 라인 유무가 model ID 유효성의 결정론적 증거입니다(R020 "attempt ≠ outcome"). 무인 루프(`/fsd`)의 stderr를 버리면 이 신호도 함께 사라집니다.
|
|
70
|
+
|
|
69
71
|
> **v2.1.223+**: `CLAUDE_CODE_DISABLE_1M_CONTEXT`가 **native 1M 창을 가진 모든 Claude 모델**을 auto-compaction으로 200K에 유지하도록 확대되었습니다(이전에는 고정 모델 목록). 미인식 model ID도 가정 컨텍스트 창 내로 유지되며 `CLAUDE_CODE_DISABLE_UNKNOWN_MODEL_WINDOW_ENFORCEMENT=1`로 복원할 수 있습니다. 위 Tier-2 표의 `claude-sonnet-5`/`claude-opus-5`(native 1M)와 `[1m]` 접미사는 이 env가 설정된 환경에서 **실효 200K로 동작**하므로, 1M 전제의 대용량 컨텍스트 위임 전에 env 설정 여부를 확인합니다(cross-ref R013 context budget).
|
|
70
72
|
|
|
71
73
|
> **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.
|
|
@@ -75,6 +75,23 @@ Subagents often report failures as "pre-existing", "baseline", or "unchanged". T
|
|
|
75
75
|
Never accept "pre-existing" without direct base-branch evidence. A false "pre-existing" claim can mask a regression introduced by the current change.
|
|
76
76
|
-->
|
|
77
77
|
|
|
78
|
+
### 원인 분석도 완료 보고와 같은 등급의 검증 대상 (Origin: #1595 #3)
|
|
79
|
+
|
|
80
|
+
위 표는 서브에이전트의 **상태 주장**("pre-existing", "unchanged")을 다루지만, 서브에이전트의 **원인 진단**도 동일 등급의 검증 대상이다. 오류 메시지 하나가 **여러 시나리오에서 동일하게 출력**될 때, 오케스트레이터와 서브에이전트가 같은 메시지를 읽고 각자 그럴듯하지만 **양립 불가능한** 원인을 구성한다. 서브에이전트의 원인 분석은 **결론이 아니라 가설**로 접수하고, 결정론적 명령으로 정정한다.
|
|
81
|
+
|
|
82
|
+
| 서브에이전트의 원인 진단 | 필요한 실측 |
|
|
83
|
+
|---|---|
|
|
84
|
+
| "대상 브랜치에 이미 tracked로 존재하며 내용이 다르다" | `git cat-file -e <branch>:<path>` (존재 여부), `git show --name-status <sha>` (추가/수정 구분) |
|
|
85
|
+
| "이 실패는 X 때문이다" (오류 메시지 해석에 근거) | 같은 메시지를 내는 **다른 시나리오**를 열거하고, 시나리오를 가르는 명령을 1개 이상 실행 |
|
|
86
|
+
| "형제/부하/환경 때문이다" | 개입 실험으로 귀속 — R010 「"플래키"는 원인이 아니다」 |
|
|
87
|
+
|
|
88
|
+
| Anti-pattern | Required |
|
|
89
|
+
|--------------|----------|
|
|
90
|
+
| 서브에이전트의 원인 분석을 결론으로 접수하고 후속 계획의 전제로 사용 | 가설로 접수 → 결정론적 명령으로 정정 후 전제화 |
|
|
91
|
+
| 오케스트레이터와 서브에이전트의 진단이 양립 불가한데 어느 쪽이 맞는지 실측 없이 한쪽 채택 | 양립 불가를 **모순 신호**로 취급 — 두 진단을 가르는 명령을 즉시 실행 |
|
|
92
|
+
|
|
93
|
+
Origin: #1595 #3 (v1.1.48 세션 — checkout 거부 메시지에 대해 mgr-gitnerd는 "develop에 tracked로 존재", 오케스트레이터는 "develop에 없음"으로 정반대 진단. `git cat-file -e` + `git show --name-status`로 정정 — 서브에이전트 분석이 틀렸고 오케스트레이터의 사실은 맞았으나 결론이 틀렸다). Cross-ref: R020 Read-Before-Characterize, R010 「참인 전제 ≠ 참인 함의」.
|
|
94
|
+
|
|
78
95
|
### Verification-Delegation Non-Termination (검증 위임 판정 종료 보장)
|
|
79
96
|
|
|
80
97
|
구조 검증(mgr-sauron R017)·판정·품질 게이트를 서브에이전트에 위임할 때, 위임 프롬프트에 **"최종 PASS/FAIL 판정 없이 turn을 종료하지 말라"**를 명시한다 — 단 이 clause는 **보조 수단**일 뿐 1차 방어선이 아니다. clause를 명시해도 mid-step 종료가 **누적 14회** 재발했다(v1.1.13/14/17/18/19 … v1.1.44, 아래 Origin 참조). **예방의 1차 방어선은 위임 경계 분할**(아래 「위임 경계를 Phase 개수로 설계」)이고, **사후 1차 방어선은 오케스트레이터의 직접 ground-truth 실측**이다.
|
|
@@ -97,6 +114,14 @@ mid-step 종료는 예상 가능한 정상 실패 모드로 취급한다 — 발
|
|
|
97
114
|
|
|
98
115
|
대조 실증(#1574, v1.1.44 세션): 단일 목표 위임(PR 생성 / 머지 / 브랜치 정리 / 버전 범프) **4건 전원 완주**, 다중 Phase 위임(mgr-sauron 3-Phase, mgr-gitnerd 3-커밋) **2건 모두 mid-step 종료**. 같은 세션에서 릴리즈 단계를 push+범프 / PR 생성 / 머지로 3분할한 것이 이 설계의 적용례다.
|
|
99
116
|
|
|
117
|
+
**배선 (R016 Rule Wiring Check)**: 이 조항의 발동 실행 경로는 `auto-dev.yaml`의 `deep-plan` / `deep-verify` 스텝이다. 파이프라인 정의가 `skill: deep-plan`처럼 **스킬 이름만** 적고 있으면, 그 정의를 따르는 것이 이 조항을 우회하는 경로가 된다 — `skill:` 값은 **분할의 근거가 되는 스킬 정의**를 가리키는 것이지 "1회 호출하라"는 지시가 아니다. 다중 Phase 스킬을 파이프라인 스텝으로 두는 정의에는 분할 지시를 **스텝 설명에 함께 기재**한다(auto-dev.yaml은 4개 사본이 CI로 동일성 강제됨).
|
|
118
|
+
|
|
119
|
+
| Anti-pattern | Required |
|
|
120
|
+
|--------------|----------|
|
|
121
|
+
| 파이프라인 정의가 `skill: <다중 Phase 스킬>`이므로 그대로 호출 | 스킬 정의의 Phase를 먼저 읽고 단일 목표 위임으로 분할해 순차 발주; 파이프라인 정의에도 분할 지시를 배선 |
|
|
122
|
+
|
|
123
|
+
Origin 보강: #1595 #4 (v1.1.48 세션 — `deep-plan`(3-Phase)이 식별 헤더만 출력하고 tool_uses=0으로 6.9초에 종료, 산출물 0 실측. `mgr-sauron`(3-Phase)은 판정 없이 종료. 두 건 모두 이 조항을 **알고 있었음에도** `auto-dev.yaml`의 스킬 지정을 따라 그대로 호출한 결과 — 텍스트는 있고 배선이 없던 사례).
|
|
124
|
+
|
|
100
125
|
Origin: #1443 (Session 126 회고 찐빠 #1) — v1.1.3 R017 검증에서 mgr-sauron이 source-hash 대조 중 판정 없이 종료 → resume 후 PASS. v1.1.4에서 "판정 반드시 출력" 명시로 1회 완료(대조 실증). **5회 재발 확인(#1492, Session 132)**: v1.1.13/14/17(clause 명시에도 재발) → v1.1.18(완료조건 6항목+종료금지 명시에도 "merging now" 한 줄 남기고 종료, 실측 결과 이미 완료) → v1.1.19(위임 프롬프트에 "4회 무시됨"까지 명시했으나 "CI 실행 중" 한 줄 남기고 종료, 실측 결과 미완료). Session 132에서 2회 모두 오케스트레이터 직접 실측으로 복구 — clause 강화가 아니라 실측 습관화가 유일하게 실증된 방어선. **누적 11회 확인(#1518 찐빠 #2, Session 136)**: v1.1.30 릴리즈 세션에서도 "완료 조건 5항목 실측 + 판정 없이 종료 금지" 명시에도 mgr-gitnerd가 "폴링 완료 통지를 기다리겠습니다" 한 줄만 남기고 종료 → 오케스트레이터 직접 실측으로 복구(lockfile push 완료 / CI pending / PR OPEN); 이번엔 "대기 중" 증상이 실제 미완료였고 Session 132의 "머지 중" 증상은 실제 완료였다는 대비로 증상→결과 추론 금지가 재확인됨. **누적 14회 + 3방향째 확인(#1574, v1.1.44 세션)**: mgr-sauron 3-Phase / mgr-gitnerd 3-커밋 위임 2건이 Phase 경계에서 종료했고(위 「위임 경계를 Phase 개수로 설계」의 대조 실증), 그중 mgr-gitnerd는 "커밋 2로 이어가겠다"고 보고했으나 실측 시 3개 커밋이 이미 전부 완료 — 실제가 보고보다 앞서는 세 번째 방향.
|
|
101
126
|
|
|
102
127
|
Cross-reference: R018 (Member Completion Verification), `feedback_release_delegation_phasing`, `feedback_orchestrator_direct_verify` (release delegation phasing을 verification 위임에도 확장).
|
|
@@ -280,15 +305,21 @@ Origin: #1266 ④.
|
|
|
280
305
|
위 파싱 레시피는 **"사용자 프롬프트 = 턴 경계"**를 암묵 전제한다. `/fsd` 같은 자율 루프는 사용자 프롬프트가 거의 없어(실측: 사용자 프롬프트 4개 대 assistant 응답 30여 회) 이 전제로는 경계 재구성이 실패하고, 계수 자체가 성립하지 않는다. 자율 루프 transcript를 셀 때는 **두 단계를 순서대로** 수행하고, 완료 전에는 **위반 횟수를 단정하지 않는다**.
|
|
281
306
|
|
|
282
307
|
1. **전처리 — `.message.role`이 존재하는 라인만 필터링**한다. 트랜스크립트에는 role 없는 라인(메타·이벤트·요약)이 assistant/user 사이에 대량으로 끼어 있어, 필터 없이는 **인접성 판정 자체가 깨진다**. 2단계의 어떤 경계 정의도 이 필터 없이는 성립하지 않는다.
|
|
283
|
-
2.
|
|
308
|
+
2. **경계 정의(규범)** — 응답 시작 경계는 **`content`에 `tool_result` 블록을 포함하지 않는 user 메시지**로 정의한다. 도구 결과도 `role: user`로 기록되므로, "user→assistant 전이 = 응답 시작"이라는 단순 정의는 도구 결과 뒤에 이어지는 assistant 메시지를 전부 새 응답으로 오인해 **위반 건수를 대폭 과대 계상**한다. 실증: 이 정의를 쓰지 않고 파싱했을 때 R007 위반이 177건으로 나왔으나, 위 규범대로 경계를 고치자 1건이 됐다(177배 과대). R008도 44 → 33으로 정정됐다.
|
|
309
|
+
|
|
310
|
+
### 자가 계수는 advisor 판정식을 재현한다
|
|
311
|
+
|
|
312
|
+
자체 해석 패턴으로 위반을 세지 말고, `.claude/hooks/scripts/r007-r008-drift-advisor.sh`의 판정식을 **1:1 재현**한다. 실증: 자체 announce 패턴을 advisor보다 좁게 잡아(번호 매긴 병렬 스폰 라인 `[N] agent:model → desc` 형식을 announce로 미포함) R008 위반을 과대 계상했다 — advisor 스크립트 자체를 참조하지 않고 "그럴듯한 정의"로 재구현하면 같은 종류의 오차가 반복된다.
|
|
284
313
|
|
|
285
314
|
| Anti-pattern | Required |
|
|
286
315
|
|--------------|----------|
|
|
287
316
|
| 자율 루프 transcript를 사용자 프롬프트 경계로 파싱해 위반 N회로 단정 | 1단계 필터 + 2단계 경계 정의를 먼저 확정; 확정 전에는 횟수 단정 금지 |
|
|
288
317
|
| 필터 없이 원본 라인 순서로 인접성을 판정 → 계수 실패를 경계 정의 탓으로 오진 | `.message.role` 필터를 먼저 적용한 뒤 경계 정의를 평가 |
|
|
289
318
|
| 경계 재구성 실패를 "위반 없음"으로 해석 | 경계 무관 지표로 대체 보고 — `┌─ Agent:` 헤더 총량, tool_use 대 announce 라인 비율 |
|
|
319
|
+
| "user→assistant 전이"를 응답 시작 경계로 단순 정의 → `tool_result`(role=user) 뒤 assistant 응답을 전부 새 응답으로 오산입 | 경계 = `tool_result` 블록을 포함하지 않는 user 메시지 (규범) |
|
|
320
|
+
| 자체 해석 패턴(예: 좁게 잡은 announce 정규식)으로 위반을 재계산 | `r007-r008-drift-advisor.sh`의 판정식을 그대로 재현 |
|
|
290
321
|
|
|
291
|
-
Origin: #1574 (v1.1.44 세션 — 자율 루프에서 R007 헤더 누락 계수를 시도했으나 사용자 프롬프트 4개로 턴 경계 재구성 불가); 1단계 필터는 #1584 #3 (v1.1.45 세션 — 위 조항을 신설했음에도 계수가 재실패. 실제 장애물은 경계 정의가 아니라 **`role=null` 라인 661개 / 전체 1215줄의 54%**였고, 필터 추가 즉시 성립(응답 시작 50, R007 위반 0) — 조항이 원인을 절반만 짚어 재발한 사례). Cross-ref: R005(계수/매칭 방법 확인 — 도구 기본 동작 미확인 시 결과 오해석).
|
|
322
|
+
Origin: #1574 (v1.1.44 세션 — 자율 루프에서 R007 헤더 누락 계수를 시도했으나 사용자 프롬프트 4개로 턴 경계 재구성 불가); 1단계 필터는 #1584 #3 (v1.1.45 세션 — 위 조항을 신설했음에도 계수가 재실패. 실제 장애물은 경계 정의가 아니라 **`role=null` 라인 661개 / 전체 1215줄의 54%**였고, 필터 추가 즉시 성립(응답 시작 50, R007 위반 0) — 조항이 원인을 절반만 짚어 재발한 사례). 경계 규범 승격 및 advisor 판정식 재현은 #1593 #6 (경계 오정의로 R007 177배 과대 계상, `tool_result` 배제 정의로 정정; R008은 좁은 announce 패턴 자가 재구현으로 44→33 정정). Cross-ref: R005(계수/매칭 방법 확인 — 도구 기본 동작 미확인 시 결과 오해석).
|
|
292
323
|
|
|
293
324
|
### Proxy Signal vs Canonical Ground-Truth (#1336 ①②)
|
|
294
325
|
|
|
@@ -14,12 +14,12 @@ 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 + SubagentStop + PostToolUse hooks | R007, R008 (`r007-r008-drift-advisor.sh` — #1229 UserPromptSubmit, #1545 SubagentStop, #1553 PostToolUse) | Reads last assistant turn; emits advisory if header/prefix absent. SubagentStop wiring (#1545) closes the no-user-input autonomous-loop gap (`/fsd`); PostToolUse (#1553) covers the orchestrator-only stretch before the first subagent spawn. Complements retroactive Stop-hook (`session-reflection.sh`, #1190). **v1.1.43부터 실제 발화 — 아래 각주 참조.** |
|
|
17
|
+
| Advisory (proactive) | UserPromptSubmit + SubagentStop + PostToolUse hooks | R007, R008 (`r007-r008-drift-advisor.sh` — #1229 UserPromptSubmit, #1545 SubagentStop, #1553 PostToolUse) | Reads last assistant turn; emits advisory if header/prefix absent. SubagentStop wiring (#1545) closes the no-user-input autonomous-loop gap (`/fsd`); PostToolUse (#1553) covers the orchestrator-only stretch before the first subagent spawn. Complements retroactive Stop-hook (`session-reflection.sh`, #1190). **v1.1.43부터 실제 발화 — 아래 각주 참조.** v1.1.49부터 역방향(announce > tool_use) 신호 포함, 기본 off 옵트인 (#1595 #6). |
|
|
18
18
|
| Advisory (telemetry) | PostToolUseFailure hook | — (계측 전용, 규칙 강제 없음) | `failure-ledger.sh` (#1561, v1.1.44) — 도구 실패를 JSONL 원장에 append. stdout/stderr 무출력이라 모델에 도달하지 않으며 절대 차단하지 않음 |
|
|
19
19
|
| Advisory (proactive) | UserPromptSubmit hook | R020 (원인 진단) | `fail-axis-cause-advisor.sh` (#1561, v1.1.44) — 원장에 실패 기록이 있는데 원인 진술 없는 재촉 프롬프트가 오면 `hookSpecificOutput.additionalContext`로 "원인 가설 되묻기" advisory 전달. 원장 부재 시 조용히 통과 |
|
|
20
20
|
| Prompt-based | CLAUDE.md + rules/ + PostCompact | All MUST rules | Behavioral guidance in context |
|
|
21
21
|
|
|
22
|
-
> **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).
|
|
22
|
+
> **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이 되고, 되돌릴 지점만 두 곳이 된다).
|
|
23
23
|
>
|
|
24
24
|
> 교훈: **배선 확인 ≠ 전달 확인 ≠ 발화 확인** — R020 "actual outcome ≠ attempt"의 훅 도메인 재현 사례.
|
|
25
25
|
|
|
@@ -50,7 +50,7 @@ If advisory enforcement proves insufficient for specific rules, these are candid
|
|
|
50
50
|
| Rule | Candidate Hook | Status | Condition for Promotion |
|
|
51
51
|
|------|---------------|--------|------------------------|
|
|
52
52
|
| R010 | git-delegation-guard.sh | Candidate | If orchestrator-direct-write violations exceed 3/session |
|
|
53
|
-
| R007/R008 | `r007-r008-drift-advisor.sh` (UserPromptSubmit #1229 + SubagentStop #1545 + PostToolUse #1553) | **Advisory implemented and firing** — proactive pre-response check wired to three trigger points; the SubagentStop leg (#1545) closes the no-user-input autonomous-loop gap (`/fsd` etc.), and the PostToolUse leg (#1553) covers the orchestrator-only stretch before the first subagent spawn. Retroactive: `session-reflection.sh` (Stop, #1190). Two-layer drift detection: proactive (#1229/#1545/#1553) + retroactive (#1190). **Historical defect (v1.1.40–v1.1.42): delivery path implemented but never fired** — #1547 (v1.1.40) switched delivery from stderr (never model-visible on exit 0) to `hookSpecificOutput.additionalContext` on JSON stdout, non-blocking (no top-level `decision`/`continue`/`stopReason`), but both scripts short-circuited BEFORE emitting: `jq -r '.role'` read a key absent at transcript top level (it is `.message.role`), so `last_assistant` was always empty and the script exited 0 silently. Measured then: 0 `"additionalContext":` occurrences across 771 transcripts; live probe emitted 0 bytes on both streams. **Resolved in v1.1.43** — parser fix on both layers + `PostToolUse` wiring, first fire confirmed by live probe (#1553). Follow-ups: v1.1.44 turn-level R008 prefix counting (#1563), v1.1.45 Skill-tool exemption (#1569). | Promote to hard-block if advisory proves insufficient (#1096) |
|
|
53
|
+
| R007/R008 | `r007-r008-drift-advisor.sh` (UserPromptSubmit #1229 + SubagentStop #1545 + PostToolUse #1553) | **Advisory implemented and firing** — proactive pre-response check wired to three trigger points; the SubagentStop leg (#1545) closes the no-user-input autonomous-loop gap (`/fsd` etc.), and the PostToolUse leg (#1553) covers the orchestrator-only stretch before the first subagent spawn. Retroactive: `session-reflection.sh` (Stop, #1190). Two-layer drift detection: proactive (#1229/#1545/#1553) + retroactive (#1190). **Historical defect (v1.1.40–v1.1.42): delivery path implemented but never fired** — #1547 (v1.1.40) switched delivery from stderr (never model-visible on exit 0) to `hookSpecificOutput.additionalContext` on JSON stdout, non-blocking (no top-level `decision`/`continue`/`stopReason`), but both scripts short-circuited BEFORE emitting: `jq -r '.role'` read a key absent at transcript top level (it is `.message.role`), so `last_assistant` was always empty and the script exited 0 silently. Measured then: 0 `"additionalContext":` occurrences across 771 transcripts; live probe emitted 0 bytes on both streams. **Resolved in v1.1.43** — parser fix on both layers + `PostToolUse` wiring, first fire confirmed by live probe (#1553). Follow-ups: v1.1.44 turn-level R008 prefix counting (#1563), v1.1.45 Skill-tool exemption (#1569), v1.1.49 reverse signal — announce > tool_use, own anchored regex, fires only when ALL tool_use blocks (Skill included) are 0, opt-in via `OMCUSTOM_R008_REVERSE=on` and NOT replicated into `session-reflection.sh` (#1595 #6). | Promote to hard-block if advisory proves insufficient (#1096) |
|
|
54
54
|
|
|
55
55
|
Promotion requires: (1) measured violation rate data, (2) user approval, (3) rollback plan.
|
|
56
56
|
|
|
@@ -114,13 +114,18 @@ The Git Push Continuation pattern (first-time strict / follow-up relaxed, scoped
|
|
|
114
114
|
| 1st explicit approval (category C, target T) | Proceed; advisory warning emitted |
|
|
115
115
|
| Follow-up same session (same C + same T) | No re-confirmation (directive persistence) |
|
|
116
116
|
| Different category or target | Fresh confirmation required |
|
|
117
|
-
| Platform
|
|
117
|
+
| Platform **permission prompt** repeats (asking to re-approve an already-allowed command) | Add a `settings.json` permission `allow` rule scoped to the specific command — this suppresses the prompt |
|
|
118
|
+
| Platform **safety classifier BLOCK** (e.g. auto-mode refuses/flags the action, not merely prompting) | `allow` rule addition is **NOT effective** — the classifier is a separate layer from the permission-prompt layer. Have the user run the command directly (`!` prefix), or remove the trigger itself (e.g. drop an unnecessary bypass flag — see R010 「우회 플래그는 우회 대상과 근거를 명시」) |
|
|
118
119
|
|
|
119
120
|
**R001 exclusion (MUST)**: R001-listed catastrophic git operations (`git reset --hard`, `git clean -fd`, `git push --force` to shared branches, `git branch -D` with unmerged commits) are EXCLUDED from this persistence rule — they always require explicit per-invocation approval regardless of prior session approvals.
|
|
120
121
|
|
|
121
122
|
**Boundary / honesty note**: This rule is ADVISORY and governs model behavior only. It CANNOT suppress Claude Code's platform-level auto-mode classifier prompts. For genuine prompt suppression on a repeated destructive command, the user must add a `settings.json` permission allow rule scoped to the specific command (e.g., a specific `supabase db push` invocation). The model SHOULD surface this workaround when the user expresses friction about repeated prompts.
|
|
122
123
|
|
|
123
|
-
|
|
124
|
+
`settings.json` **allow 규칙은 permission prompt를 억제하지만, safety classifier 차단은 억제하지 못한다 — 서로 다른 층이다** (#1592). 실효 경로는 두 가지뿐이다: (a) 사용자가 직접 실행, (b) 차단 트리거 자체를 제거(예: 불필요한 `--admin` 제거). 부가로, CC v2.1.229+는 위험 플래그(`--force`/`--amend`/`--no-verify`)를 가진 git/gh 명령을 auto-approve하지 않는다(설치 버전 실측 v2.1.233).
|
|
125
|
+
|
|
126
|
+
> Origin: #1592 (v1.1.47 세션 실측) — `permissions.allow`에 `Bash(gh:*)`와 `defaultMode: bypassPermissions`가 있는데도 `--admin` 포함 머지 위임이 auto-mode classifier에 차단됐다. `Edit(.claude/**)` allow 규칙이 있는데도 `.claude/settings.local.json` 편집이 차단됐다. 두 사례 모두 allow 규칙이 걸어둔 permission-prompt 층을 이미 통과한 상태에서 별도의 classifier 층이 차단한 것 — allow 규칙 추가로는 해소되지 않았고, 실효 해법은 (a) `--admin` 제거(R010 선행 실측 조항, #1591), (b) 사용자 직접 실행이었다.
|
|
127
|
+
|
|
128
|
+
Cross-references: R001 (safety — destructive operation pre-checks still apply), R002 (permission tiers), R010 (우회 플래그 선행 실측 — 차단 트리거 제거 경로). Reference issues: #1230, #1226 (item 2), #1592.
|
|
124
129
|
|
|
125
130
|
## User-Provided Input Precedence
|
|
126
131
|
|