oh-my-customcode 1.1.48 → 1.1.50

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 (47) hide show
  1. package/README.md +7 -6
  2. package/dist/cli/index.js +171 -294
  3. package/dist/index.js +65 -65
  4. package/package.json +2 -2
  5. package/templates/.claude/agents/agora-runner.md +114 -0
  6. package/templates/.claude/hooks/hooks.json +10 -0
  7. package/templates/.claude/hooks/scripts/agent-teams-advisor.sh +6 -1
  8. package/templates/.claude/hooks/scripts/claude-md-reinject.sh +65 -0
  9. package/templates/.claude/hooks/scripts/r007-r008-drift-advisor.sh +41 -3
  10. package/templates/.claude/hooks/scripts/session-env-check.sh +29 -3
  11. package/templates/.claude/rules/MAY-optimization.md +7 -1
  12. package/templates/.claude/rules/MUST-agent-design.md +15 -4
  13. package/templates/.claude/rules/MUST-agent-teams.md +19 -0
  14. package/templates/.claude/rules/MUST-completion-verification.md +53 -0
  15. package/templates/.claude/rules/MUST-continuous-improvement.md +20 -4
  16. package/templates/.claude/rules/MUST-enforcement-policy.md +7 -3
  17. package/templates/.claude/rules/MUST-orchestrator-coordination.md +45 -0
  18. package/templates/.claude/rules/MUST-parallel-execution.md +61 -8
  19. package/templates/.claude/rules/MUST-permissions.md +8 -0
  20. package/templates/.claude/rules/MUST-safety.md +6 -0
  21. package/templates/.claude/rules/MUST-sync-verification.md +21 -0
  22. package/templates/.claude/rules/MUST-tool-identification.md +22 -4
  23. package/templates/.claude/rules/SHOULD-ecomode.md +4 -0
  24. package/templates/.claude/rules/SHOULD-error-handling.md +2 -0
  25. package/templates/.claude/rules/SHOULD-interaction.md +4 -0
  26. package/templates/.claude/rules/SHOULD-memory-integration.md +2 -0
  27. package/templates/.claude/rules/SHOULD-verification-ladder.md +2 -0
  28. package/templates/.claude/skills/agora/SKILL.md +325 -0
  29. package/templates/.claude/skills/agora/scripts/agora.sh +761 -0
  30. package/templates/.claude/skills/agora/scripts/anonymize.sh +492 -0
  31. package/templates/.claude/skills/agora/scripts/judge.sh +427 -0
  32. package/templates/.claude/skills/agora/scripts/response-schema.json +26 -0
  33. package/templates/.claude/skills/agora/scripts/reviewers.sh +312 -0
  34. package/templates/.claude/skills/agora/scripts/verdict-schema.json +34 -0
  35. package/templates/.claude/skills/hada-scout/SKILL.md +1 -1
  36. package/templates/.claude/skills/help/SKILL.md +1 -1
  37. package/templates/.claude/skills/pipeline/workflows/auto-dev.yaml +21 -3
  38. package/templates/.claude/skills/sauron-watch/SKILL.md +1 -1
  39. package/templates/.claude/skills/status/SKILL.md +3 -3
  40. package/templates/.claude/skills/token-efficiency-audit/SKILL.md +1 -1
  41. package/templates/CLAUDE.md +3 -3
  42. package/templates/CLAUDE.md.en +3 -3
  43. package/templates/CLAUDE.md.ko +3 -3
  44. package/templates/README.md +5 -5
  45. package/templates/guides/agent-eval/README.md +1 -1
  46. package/templates/manifest.json +3 -3
  47. package/templates/workflows/auto-dev.yaml +21 -3
@@ -15,6 +15,8 @@ model: sonnet # CC-native alias (Tier 1) or full model ID (Tier 2)
15
15
  tools: [Read, Write, ...] # Allowed tools
16
16
  ```
17
17
 
18
+ > **v2.1.239+**: `.md` 파일이 UTF-8 BOM으로 시작하는 agent/skill/command 파일이 **조용히 무시**되던 결함이 수정되었습니다. 구버전에서는 BOM이 있는 `.claude/agents/*.md`, `.claude/skills/*/SKILL.md`가 에러 없이 로드에서 누락됐습니다 — "에이전트/스킬이 없다"는 관측이 실제로는 "BOM 때문에 무음 스킵"일 수 있었습니다. R017 Count Sync가 실측하는 카운트는 파일 **존재**를 세지만, BOM 파일은 CC가 실제로 **로드하지 않았으므로** 구버전에서는 카운트와 실제 로드된 에이전트/스킬 수가 어긋날 수 있었습니다(cross-ref R017 Count Sync).
19
+
18
20
  <!-- ARCHIVED CC version note (historical):
19
21
  > **v2.1.208+**: The Agent tool no longer launches with no tools when a subagent's `tools:` list resolves to nothing — it now returns a clear error naming the unrecognized entries, catching frontmatter `tools:` typos that previously failed silently.
20
22
  -->
@@ -66,8 +68,14 @@ Skill/rule text instructing "spawn with `model: opus`" refers to this tier — a
66
68
 
67
69
  > **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
70
 
71
+ > **v2.1.247+**: sub-agent가 첫 호출에서 model 404(인식 불가 model ID)를 만나면 죽던 결함이 수정되어, 이제 세션의 fallback model chain을 사용합니다. 부모 세션에 전달되는 에러에도 error type/status/request id/model이 포함됩니다. 위 v2.1.233 print모드 `unrecognized_model` 진단 노트가 관측성만 다뤘다면, 이 수정은 **실행 연속성**을 추가합니다 — 구버전에서는 서브에이전트 모델 해석 실패가 fallback 없이 그대로 죽음으로 이어졌습니다.
72
+
73
+ > **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를 버리면 이 신호도 함께 사라집니다.
74
+
69
75
  > **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
76
 
77
+ > **v2.1.251+**: `CLAUDE_CODE_SUBAGENT_MODEL`이 이제 "모든 것을 override"가 아니라 **기본 subagent 모델만 설정**합니다 — 에이전트 정의의 `model:`(Tier 1/2)과 spawn 시점 명시적 `model`(Tier 3)이 이 env보다 **우선**합니다. 이 저장소는 다수 에이전트가 Tier-2 full ID로 model을 pin하므로, v2.1.251부터는 이 env var가 project의 model pin을 더 이상 깨뜨릴 수 없습니다(단, 이전 버전에서 실행된 세션은 여전히 영향받았을 수 있습니다).
78
+
71
79
  > **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.
72
80
 
73
81
  <!-- ARCHIVED CC version notes (historical):
@@ -75,7 +83,7 @@ Skill/rule text instructing "spawn with `model: opus`" refers to this tier — a
75
83
 
76
84
  > **v2.1.197+**: Claude Sonnet 5가 Claude Code의 **기본 모델**로 도입되었습니다 — 네이티브 1M-token 컨텍스트, 프로모션 가격 $2/$10 per Mtok(2026-08-31까지). frontmatter에서 명시 opt-in하려면 Tier-2 full ID `claude-sonnet-5`를 사용합니다(`sonnet5`는 어느 계층에서도 유효한 값이 아님 — 위 3-Tier 구분 참조). **정정(실측)**: 이 조항이 이전에 "oh-my-customcode의 base `sonnet` alias는 안정성을 위해 `claude-sonnet-4-6`에 고정 유지"라고 서술했으나 사실이 아니다 — `sonnet` alias 해석 주체는 CC이며 프로젝트가 pin할 수 없다(Tier 1 참조); frontmatter `model: sonnet` 에이전트가 실측상 `claude-sonnet-5`로 실행되었다. Sonnet 5가 CC 신규 기본값이므로 명시 모델 없는 세션은 이제 Sonnet 5에서 동작합니다.
77
85
 
78
- > **v2.1.201+**: Claude Sonnet 5 세션이 harness reminder를 mid-conversation system role로 주입하지 않도록 변경되었습니다 — Sonnet 5 실행 시 하니스 리마인더(규칙 재주입 등) 전달 방식이 조정되었으며, PostCompact 규칙 재주입(R021)·세션 연속성 동작 자체에는 영향이 없습니다. Sonnet 5가 CC 기본 모델(v2.1.197+)이므로 명시 모델 없는 세션에 적용됩니다.
86
+ <!-- RETIRED (은퇴 릴리즈 v1.1.50, 보존 기준 v2.1.230 미만): > **v2.1.201+**: Claude Sonnet 5 세션이 harness reminder를 mid-conversation system role로 주입하지 않도록 변경되었습니다 — Sonnet 5 실행 시 하니스 리마인더(규칙 재주입 등) 전달 방식이 조정되었으며, PostCompact 규칙 재주입(R021)·세션 연속성 동작 자체에는 영향이 없습니다. Sonnet 5가 CC 기본 모델(v2.1.197+)이므로 명시 모델 없는 세션에 적용됩니다. -->
79
87
  -->
80
88
 
81
89
  > **Fable 5 Effort 전략**: Fable 5는 **high effort가 기본값**이며, `xhigh`는 capability-sensitive 작업(최고난도 아키텍처/추론)에 한정해야 합니다. Fable 5의 `low`/`medium` effort조차 이전 세대 모델의 `xhigh`를 상회하는 품질을 보이므로, Fable 5를 사용하는 실행 에이전트는 `effort` 필드를 신중히 명시하고 불필요한 `xhigh` 남용을 지양합니다(R005 비용/지연 인식과 정합).
@@ -124,7 +132,7 @@ This is a settings-level resilience mechanism, distinct from the per-agent `mode
124
132
 
125
133
  ### Optional Frontmatter
126
134
 
127
- Key optional fields: `memory`, `effort`, `skills`, `soul`, `isolation`, `background`, `maxTurns`, `maxTokens`, `mcpServers`, `hooks`, `permissionMode`, `disallowedTools`, `limitations`, `domain`, `disableSkillShellExecution`. Supported since CC v2.1.63+. See full optional frontmatter via Read tool.
135
+ 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.
128
136
 
129
137
  ### Note on `skills:` field
130
138
 
@@ -165,6 +173,8 @@ limitations: # Negative capability declarations
165
173
  - "cannot modify code"
166
174
  domain: backend # backend | frontend | data-engineering | devops | universal
167
175
  disableSkillShellExecution: true # Disable inline shell execution in skills (v2.1.91+)
176
+ experimental:
177
+ cacheTtl: "5m" # "5m" | "1h" — subagent prompt cache TTL when not otherwise set (v2.1.248+)
168
178
  ```
169
179
 
170
180
  > **Note**: When `disableSkillShellExecution` is enabled (v2.1.91+), skills that rely on inline shell execution (e.g., `rtk-exec`) will have their shell blocks disabled. This is a security hardening option.
@@ -179,7 +189,8 @@ Hook JSON output `terminalSequence` field for desktop notifications, window titl
179
189
 
180
190
  ## Hook Event Types
181
191
 
182
- 31 event types supported: SessionStart, Setup, UserPromptSubmit, UserPromptExpansion, PreToolUse, PermissionRequest, PermissionDenied, PostToolUse, PostToolUseFailure, PostToolBatch, Notification, MessageDisplay, SubagentStart, SubagentStop, TaskCreated, TaskCompleted, Stop, StopFailure, TeammateIdle, InstructionsLoaded, ConfigChange, CwdChanged, DirectoryAdded, FileChanged, WorktreeCreate, WorktreeRemove, PreCompact, PostCompact, Elicitation, ElicitationResult, SessionEnd. 4 handler types: command, prompt, http, agent. See full reference table via Read tool.
192
+ 33 event types supported: SessionStart, Setup, UserPromptSubmit, UserPromptExpansion, PreToolUse, PermissionRequest, PermissionDenied, PostToolUse, PostToolUseFailure, PostToolBatch, Notification, MessageDisplay, SubagentStart, SubagentStop, TaskCreated, TaskCompleted, Stop, StopFailure, TeammateIdle, InstructionsLoaded, ConfigChange, CwdChanged, DirectoryAdded, FileChanged, WorktreeCreate, WorktreeRemove, PreCompact, PostCompact, Elicitation, ElicitationResult, SessionEnd, PreModelSwitch, PostModelSwitch (v2.1.251+). 4 handler types: command, prompt, http, agent. See full reference table via Read tool.
193
+ > **v2.1.251+**: 신규 훅 이벤트 `PreModelSwitch`/`PostModelSwitch`가 추가되어 model switch를 block/confirm/annotate할 수 있습니다. 또한 `SessionStart` resume 훅이 이제 session staleness와 예상 re-cache 비용을 인자로 받습니다.
183
194
 
184
195
  > **`MessageDisplay`는 표시 전용 — `additionalContext` 미지원**: `MessageDisplay`는 `hookSpecificOutput.displayContent`로 **화면 표시 텍스트만** 교체하며, 트랜스크립트와 Claude가 보는 내용은 원본이 유지된다. 따라서 advisory 훅을 `MessageDisplay`에 배선하면 **모델에 도달하지 않는다**. `additionalContext`(모델 컨텍스트 주입)를 지원하는 이벤트는 SessionStart, Setup, SubagentStart, UserPromptSubmit, UserPromptExpansion, PreToolUse, PostToolUse, PostToolUseFailure, PostToolBatch, Stop, SubagentStop이다. (이전 판이 나열하던 `PostMessage`는 문서화된 이벤트가 아니다 — 실제 이벤트명은 `MessageDisplay`.)
185
196
 
@@ -275,7 +286,7 @@ Agent frontmatter `hooks:` now fire when the agent runs as a main-thread agent v
275
286
  -->
276
287
 
277
288
  <!-- ARCHIVED CC version note (historical):
278
- > **v2.1.204+**: headless 세션의 SessionStart hook 중 hook 이벤트가 스트리밍되지 않아 remote worker가 hook 도중 idle-reap되던 문제가 수정되었습니다. Hook Event Types/SessionStart 관련.
289
+ <!-- RETIRED (은퇴 릴리즈 v1.1.50, 보존 기준 v2.1.230 미만): > **v2.1.204+**: headless 세션의 SessionStart hook 중 hook 이벤트가 스트리밍되지 않아 remote worker가 hook 도중 idle-reap되던 문제가 수정되었습니다. Hook Event Types/SessionStart 관련. -->
279
290
  -->
280
291
 
281
292
  ## Permission Mode Guidance
@@ -411,6 +411,25 @@ Cross-reference: R020 ("actual outcome ≠ attempt" — verifying that a command
411
411
 
412
412
  > **v2.1.222+**: `SendMessage`가 긴 summary를 문자 수 제한으로 거부하던 동작이 **절단(truncate)**으로 변경되어 전송이 실패하지 않습니다. 전송 실패가 사라진 대신 **조용한 절단**이라는 새 실패 모드가 생겼으므로, 위 표의 "SendMessage report = Low reliability" 원칙이 오히려 강화됩니다. 긴 보고가 필요하면 SendMessage 본문 대신 아티팩트 파일 경로 전달(R006 Artifact Channel Protocol)로 대체합니다.
413
413
 
414
+ > **★★ v2.1.246+**: `maxTurns` 한도에 도달해 멈춘 서브에이전트의 결과가 이제 **partial로 표시**되고 `SendMessage`로 이어가라는 힌트가 붙습니다 — **이전에는 완료된 것처럼 보였습니다.**
415
+ >
416
+ > **확정된 것 (v1.1.50 세션 실측)**: `maxTurns` 절단은 R020 「Verification-Delegation Non-Termination」이 누적 14회로 기록한 "서브에이전트가 판정 없이 turn을 종료" 증상의 **실재하는 원인 중 하나**다 — 더 이상 가설이 아니다. v1.1.50 릴리즈 세션에서 오케스트레이터가 4개 그룹을 병렬 위임했고, 그중 **3개 그룹이 20턴 `maxTurns` 한도로 절단**되어 통지에 `stopped at its 20-turn limit (partial result)`이 명시됐다:
417
+ > - 한 건은 문장 중간에서 절단(진행도 불명 — 실측 필요).
418
+ > - 다른 한 건은 "Templates 미러를 동기화합니다" 직후 절단 — 오케스트레이터는 이 문구로 **미실행**을 추정했으나, 재개 후 실측 결과 작업은 **이미 완료돼 있었다**. 절단 위치(마지막 출력 문장)로부터 진행도를 추론하는 것 자체가 불가능함을 재확인한 사례다 — R020 「증상만으로 결과를 넘겨짚지 않는다」의 세 방향 중 "(c) 실제가 보고보다 앞섬"의 재현.
419
+ > - 세 번째 건은 "Now R020 — three items. Let's find suitable locations."라는 **다음 작업 예고 직후** 절단 — 착수 여부조차 미실측 상태로 끊겼다.
420
+ >
421
+ > **v2.1.246 이전이었다면 이 partial 표시가 없어 세 건 모두 완료 보고로 읽혔을 것이다.**
422
+ >
423
+ > **확정되지 않은 것**: R020이 기록한 과거 14회 각각이 이 원인이었는지는 미검증이다 — 사례별 귀속은 turn 수·소요 시간을 `maxTurns` 한도와 대조하는 별도 검증이 필요하다.
424
+ >
425
+ > **행동 함의**: 이 원인은 R020의 clause 강화("판정 없이 종료하지 말라")가 14회 내내 실패했던 이유를 설명한다 — **절단 주체가 에이전트의 판단이 아니라 플랫폼의 turn 한도이면, 에이전트를 향한 지시는 애초에 닿지 않는다.** 대칭적으로 R020 「위임 경계를 Phase 개수로 설계」(단일 목표로 분할)가 효과적이었던 이유도 설명된다 — 작업이 작으면 `maxTurns` 안에서 자연히 끝나기 때문이지, 에이전트가 더 순종적이어서가 아니다.
426
+ >
427
+ > **낮추지 말 것**: 원인이 확정됐다고 해서 위 표의 결정론적 ground-truth 검증 원칙을 낮추지 않는다 — 절단이 아닌 원인(위임 경계 미분할, 에이전트 자체 판단 종료)도 계속 존재한다. 또한 **partial 표시는 v2.1.246 이상에서만 나타나므로**, 그 이전 버전에서 관측된 mid-step 종료 사례를 재해석할 때는 이 신호 자체가 부재했다는 것을 전제로 한다 — "partial 표시가 없었다"가 "maxTurns 절단이 아니었다"의 증거는 아니다.
428
+
429
+ > **v2.1.251+**: Agent Teams 팀원의 최종 답변이 팀 리드에 도달하지 못하던 결함이 수정되어, 이제 idle notification에 실려 도착합니다(이전에는 내용 없는 "available" 알림만 떴습니다). 위 v2.1.224 "SendMessage teammate inbox 쓰기 실패 시에도 Message sent로 보고" 수정의 **후속 실증**입니다 — 구버전에서는 팀원이 정상 완료해도 리드가 그 답변을 못 받을 수 있었으므로, "결정론적 ground-truth로 확인"이라는 위 표의 원칙이 이 시점 이전 세션에서는 특히 중요했습니다.
430
+
431
+ > **v2.1.234+**: `/config`의 "Default teammate model" 설정이 **제거**되어, agent-team teammate는 이제 spawn이 모델을 지정하지 않는 한 **leader의 모델**을 사용합니다. 이전에는 teammate 모델을 전역 설정값으로 지정할 수 있었으므로, 과거 세션의 "teammate가 어떤 모델로 실행됐는지" 서술은 이 변경 이전 버전 기준일 수 있습니다.
432
+
414
433
  <!-- ARCHIVED CC version note (historical):
415
434
  > **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.
416
435
 
@@ -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,10 +114,30 @@ 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 위임에도 확장).
103
128
 
129
+ #### maxTurns 절단 실증 (Origin: v1.1.50 세션)
130
+
131
+ **실측 (v1.1.50 세션)**: 오케스트레이터가 4개 그룹을 병렬 위임했고 **그중 3개가 20턴 `maxTurns` 한도로 절단**됐다. 세 건 모두 통지에 `stopped at its 20-turn limit (partial result)`이 명시됐고 출력이 작업 중간에서 끊겼다 — 한 건은 문장 중간에서 절단(진행도 불명, 실측 필요), 한 건은 "Templates 미러를 동기화합니다"라고 예고한 직후 절단(오케스트레이터는 미실행으로 추정했으나 **실측 결과 미러 동기화까지 이미 완료**돼 있었다 — 위 「증상만으로 결과를 넘겨짚지 않는다」의 "(c) 실제가 보고보다 앞섬" 재현), 한 건은 "Now R020 — three items"라고 다음 작업을 예고한 직후 절단(실측 결과 **편집은 완료, 검증만 미수행** 상태였다). 세 건 모두 **절단 위치 문장과 실제 진행도가 어긋났다** — 이것이 이 실증의 핵심이다.
132
+
133
+ 1. **확정**: `maxTurns` 절단은 이 조항이 누적 14회로 기록한 "판정 없이 종료" 증상의 **실재하는 원인 중 하나**다. CC v2.1.246부터 partial로 표시되므로 이제 **관측 가능**하다(그 이전에는 완료로 보였다 — R018 v2.1.246 노트 교차참조).
134
+ 2. **미확정**: 과거 14회 **각각**이 이 원인이었는지는 미검증이다. 사례별 귀속에는 turn 수·소요 시간 대조가 필요하다.
135
+ 3. **설명력**: 이것은 **왜 clause 강화가 14회 내내 실패했는지**를 설명한다 — 에이전트에게 "종료하지 말라"고 지시해도 **절단 주체가 에이전트가 아니면 지시가 닿지 않는다**. 역으로 「위임 경계를 Phase 개수로 설계」가 효과적이었던 이유도 설명된다: 작업이 작으면 턴 한도 안에 끝나기 때문이지 에이전트가 더 순종적이어서가 아니다.
136
+ 4. **정량 기준 신설**: 위임 크기 판정을 "Phase가 몇 개인가"에서 **"필요 tool call이 20턴 안에 들어가는가"**로 바꾼다. 파일 1개당 Read + Edit + 미러 Edit + diff 확인 = 약 4턴이므로, **파일 편집형 위임은 담당 파일 4~5개가 실질 상한**이다. v1.1.50 세션의 절단 3건은 담당 파일이 각각 4개·3개·7개였고 파일당 신규 노트 추가·은퇴 판정·미러 동기화를 함께 요구했다 — 산술적으로 20턴에 들어갈 수 없는 위임이었다. **이는 에이전트의 실패가 아니라 오케스트레이터의 위임 설계 결함이다.**
137
+ 5. **완화책**: 미러 동기화처럼 **후행 필수 작업은 마지막에 몰지 말고 파일 단위로 즉시 수행**한다 — 절단은 항상 마지막 작업을 자르므로, 마지막에 몰린 작업은 절단 시 전량 유실된다.
138
+
139
+ Cross-reference: R018 (v2.1.246 maxTurns partial-marking 노트), R009 (Member Prompt Size Cap — 프롬프트 토큰 상한과 별개로 턴 수 상한도 위임 크기 설계 변수임을 추가).
140
+
104
141
  <!--
105
142
  > **v2.1.199+**: subagent가 API 오류(usage limit reached 등)를 성공 결과로 오보하던 문제가 수정되어 이제 오류가 parent agent에 정확히 보고됩니다. 플랫폼 수정으로 false-success 자가보고 빈도는 줄지만, "actual outcome ≠ attempt" ground-truth 검증 원칙(R020 Core Rule)은 여전히 유지된다 — subagent 보고를 그대로 신뢰하지 말고 `git status`/`grep`/validation script로 재확인한다.
106
143
 
@@ -273,6 +310,8 @@ Origin: #1266 ④.
273
310
 
274
311
  실증: 2026-07-30 세션에서 자가 보고는 "직전 두 응답에서 누락"(2회)이었으나 transcript 실측은 **7회**였다 — 3.5배 과소 계상. Origin: #1553 찐빠 #2.
275
312
 
313
+ **계수를 수행하지 않았다면 그 사실을 명시할 것 (Origin: #1601, v1.1.49 세션)**: 시간·비용 제약으로 transcript 파싱 계수를 생략하는 경우, 회고 자체에 "전수 계수 미수행"임을 밝혀야 한다. 계수하지 않은 회고의 항목 목록은 위반 전수가 아니라 **"진행 중 자각했거나 서브에이전트가 지적한 항목"에 한정**되며, 이를 밝히지 않으면 독자가 목록을 전수로 오해해 후속 조치 우선순위가 왜곡된다. v1.1.49 세션 회고는 계수를 수행하지 않았고 그 사실을 스스로 명시했다(좋은 사례) — 대조적으로 그 이전 세션(위 실증)은 계수 미수행 여부를 밝히지 않은 기억 기반 자가 보고였고 실측 대비 3.5배 과소 계상이었다.
314
+
276
315
  이는 Read-Before-Characterize의 **자기 적용** 각도다 — 진단 대상이 외부 로그가 아니라 자기 자신의 transcript일 때에도 "읽기 전 특성화 금지"가 동일하게 적용된다.
277
316
 
278
317
  #### 자율 루프 세션의 턴 경계 정의 (계수 전 확정 필수)
@@ -350,6 +389,20 @@ Session 106: during 529 buffering, a CHANGELOG was misdiagnosed as "61x 중복
350
389
 
351
390
  Origin: #1269 ① (R020 self-violation, session 106).
352
391
 
392
+ ### Failure/Interrupt Report ≠ Actual Failure (reverse direction)
393
+
394
+ 위 항목들은 대체로 "성공 보고 ≠ 실제 성공"을 다루지만, **역방향**도 동일하게 검증 대상이다 — "실패/중단 보고"를 받았을 때도 ground-truth를 확인하기 전에는 실제로 실패했다고 단정하지 않는다.
395
+
396
+ | 증상 | 실제 상태 | 확인 수단 |
397
+ |------|-----------|-----------|
398
+ | **v2.1.246+**: 매우 큰 기존 파일을 덮어쓴 뒤 Write 도구가 "Out of memory"를 보고하거나 오래 멈춤 | **파일 자체는 정상적으로 쓰여 있었다** | 도구의 실패 보고 대신 파일 내용/크기를 직접 재확인 |
399
+ | **v2.1.246+**: 헤드리스/원격 세션에서 수신 메시지로 인터럽트된 MCP 도구 호출이 "출력 없이 완료됨"으로 보고됨(v2.1.246 이전) | 실제로는 **인터럽트**됐다 — 정상 완료가 아니었다 | v2.1.246+는 명시적 interrupted 에러로 보고하도록 수정됨; 구버전 세션의 "빈 출력 완료"는 무음 인터럽트였을 수 있음 |
400
+ | **v2.1.246+**: 실행 중 인터럽트된 셸 명령이 "Ran 1 shell command"로만 표시(잘렸다는 표시 없음, v2.1.246 이전) | 명령이 **완주하지 못했다** | 출력 완결성을 별도로 확인(예상 출력 패턴 대조) 없이 "실행됨"만으로 성공 단정 금지 |
401
+
402
+ > **v2.1.234+**: print/SDK 모드에서 SIGTERM 수신 시 더 이상 interrupted turn이나 synthetic tool denial을 기록하지 않는다(명령은 여전히 종료되고 프로세스는 exit code 143). 무인 실행(`-p` 모드) 강제 종료 후 트랜스크립트를 완료 판정 근거로 쓸 때, v2.1.234+에서는 SIGTERM에 의한 중단이 트랜스크립트 상에 "interrupted"로 남지 않는다는 점을 전제해야 한다 — 트랜스크립트가 깨끗해 보여도 실제로는 SIGTERM으로 잘렸을 수 있다.
403
+
404
+ **교훈**: 위 Core Rule("actual outcome ≠ attempt")은 방향이 없다 — 도구가 성공을 보고하든 실패를 보고하든, 보고 자체는 ground-truth가 아니다. 실패 보고를 받았다고 곧바로 재시도·롤백에 들어가지 말고, 먼저 실제 산출물 상태를 확인한다.
405
+
353
406
  ### CI Publish-Step Error vs Published-Artifact Ground Truth
354
407
 
355
408
  > 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.
@@ -95,21 +95,37 @@ R016의 승격 루프(위반 지적 → 규칙 조항 추가)는 코퍼스의 **
95
95
 
96
96
  ### 버전노트 보존정책
97
97
 
98
- - 규칙 내 CC 버전노트(`> **v2.1.NNN+**:`)는 최근 2-3개 마이너 릴리즈(현행 기준 v2.1.212 이상)만 visible 유지한다.
98
+ - 규칙 내 CC 버전노트(`> **v2.1.NNN+**:`)는 최근 2-3개 마이너 릴리즈(현행 기준 v2.1.230 이상)만 visible 유지한다.
99
99
  - 그 이하 버전노트는 HTML-comment화(무손실 중간 단계) 하거나 `guides/claude-code/15-version-compatibility.md`로 이관한다.
100
100
  - `claude-native` 스킬이 생성하는 버전 추적 이슈를 규칙에 반영할 때, 최신만 visible로 두고 구버전은 즉시 은닉한다.
101
+ - **기준선은 고정 상수가 아니라 최신 CC 대비 상대 폭으로 유지한다**: 기준선 v2.1.212가 설정될 당시 CC 최신은 v2.1.233이었으므로 보존 폭은 약 21 patch였다. 이번 상향(v1.1.50, `claude --version` = `npm view @anthropic-ai/claude-code version` 실측 = v2.1.251) 시점에 같은 폭을 유지하려면 기준선이 v2.1.230이어야 한다 — 기준선 갱신 시 "최신 실측값 − 약 20 patch"로 재계산할 것.
102
+
103
+ #### 은퇴 판정 기준 (기준선 미만 ≠ 자동 은퇴)
104
+
105
+ 기준선 미만은 은퇴 **검토 대상**을 정의할 뿐, 은퇴 **여부**를 자동으로 결정하지 않는다. 기준선 미만 노트는 다음 3개 조건을 **모두** 충족할 때만 은퇴(HTML-comment화)한다 — 하나라도 걸리면 **유지**하고, 유지 판정과 사유를 스윕 기록에 남긴다.
106
+
107
+ | 조건 | 판정 |
108
+ |------|------|
109
+ | (a) 인용 부재 | 다른 어떤 visible 노트도 그것을 "같은 계열"(cf., 연장선, 인접 등)로 인용하지 않는가 |
110
+ | (b) 비현행 | 현행 동작을 규정하지 않는가 (예: 이미 롤백/재수정된 과거 상태 서술) |
111
+ | (c) 진단 함의 소멸 | 회고적 진단 함의(과거 관측 재해석 근거)가 더 이상 없는가 |
112
+
113
+ 세 조건 모두 참 → 은퇴. 하나라도 거짓 → 유지(anchor로 인용되거나, 현행 동작을 서술하거나, 진단 함의가 살아있는 노트는 기준선 미만이어도 보존 가치가 있다). 이 기준은 v1.1.50 4개 병렬 그룹의 실제 판정에서 역추출한 것이다(아래 실적 참조) — "기준선 미만 = 즉시 은퇴"로 문자 그대로 읽으면 이 기준과 모순된다.
101
114
 
102
115
  #### 보존 기준 변경 = 전 룰 파일 스윕 (같은 릴리즈 내 필수)
103
116
 
104
- 보존 기준선을 상향하면 **같은 릴리즈에서 23개 룰 파일 전수를 스윕**해 기준 미만 노트를 HTML-comment화한다. 기준만 올리고 적용을 다음 릴리즈로 이월하면 코퍼스가 기준과 불일치한 상태로 남고, 그 불일치는 다음 회고에서 "잔존 N건" 부채로 재발견될 때까지 보이지 않는다. 스윕 범위는 `.claude/rules/**`와 `templates/.claude/rules/**` 양쪽이며, 잔존 여부는 **HTML 주석 안/밖을 구분해** 실측한다 — 단순 `grep`은 이미 은퇴한 주석 내부 노트까지 세어 판정을 왜곡한다.
117
+ 보존 기준선을 상향하면 **같은 릴리즈에서 23개 룰 파일 전수를 스윕**한다 — "스윕"은 기준 미만 노트의 **전량 HTML-comment화**가 아니라, 위 「은퇴 판정 기준」 3조건에 따른 **전수 검토**(각 노트를 은퇴/유지로 판정하고 유지 시 사유를 기록)를 의미한다. 기준만 올리고 검토 자체를 다음 릴리즈로 이월하면 코퍼스가 기준과 불일치한 상태로 남고, 그 불일치는 다음 회고에서 "잔존 N건" 부채로 재발견될 때까지 보이지 않는다 — **이월 금지는 변하지 않는다**, 변하는 것은 "전수 은퇴"가 아니라 "전수 판정"이 의무라는 점이다. 스윕 범위는 `.claude/rules/**`와 `templates/.claude/rules/**` 양쪽이며, 잔존 여부는 **HTML 주석 안/밖을 구분해** 실측한다 — 단순 `grep`은 이미 은퇴한 주석 내부 노트까지 세어 판정을 왜곡한다.
105
118
 
106
119
  | Anti-pattern | Required |
107
120
  |--------------|----------|
108
- | 보존 기준선만 상향하고 기존 노트 스윕을 다음 릴리즈로 이월 | 기준 상향과 전 룰 파일 스윕을 같은 릴리즈에서 완료 |
109
- | `grep -c` 히트 수로 잔존 판정 | 주석 안/밖을 구분해 **visible 잔존**만 계수 |
121
+ | 보존 기준선만 상향하고 기존 노트 검토를 다음 릴리즈로 이월 | 기준 상향과 전 룰 파일의 **전수 판정**(은퇴/유지 + 유지 사유 기록)을 같은 릴리즈에서 완료 |
122
+ | 기준선 미만 노트를 판정 없이 전량 HTML-comment화 | 「은퇴 판정 기준」 3조건(인용 부재 AND 비현행 AND 진단 함의 소멸)을 적용해 항목별 판정 |
123
+ | `grep -c` 히트 수로 잔존 판정 | 주석 안/밖을 구분해 **visible 잔존**만 계수 — 잔존 자체는 결함이 아니다(유지 판정의 결과일 수 있음) |
110
124
 
111
125
  Origin: #1563 찐빠 #4 — R016이 보존 기준을 v2.1.212로 규정했으나 R001/R005/R012에 visible v2.1.208 노트 3건이 잔존해 v1.1.44에서 뒤늦게 은퇴. Cross-reference: R005(HTML-comment 컨텍스트 최적화), R017(Count Sync — 전수 grep + 의미 판별).
112
126
 
127
+ **실적 (v1.1.50)**: 기준선을 v2.1.212→v2.1.230으로 상향할 때, 룰 파일 소유권을 4개 병렬 그룹으로 분배해 각 그룹이 자기 담당 파일만 스윕했다 — 스윕을 **작업 종류**(예: "은퇴 담당" vs "신규 노트 담당")가 아니라 **파일 소유권**으로 분배해야 병렬 에이전트 간 동일 파일 동시 편집 충돌이 발생하지 않는다(R009 File-Disjoint 원칙의 룰 코퍼스 자체 적용 사례). 스윕 결과는 **은퇴 2건 / 유지 다수**(기준선 미만 visible 노트 49건이 11개 파일에 잔존 — 전수 검토 후 유지 판정) — 은퇴된 2건은 어떤 visible 노트도 인용하지 않는 순수 이력 서사(R006 v2.1.201/204: v2.1.201 Sonnet 5 harness reminder 전달방식, v2.1.204 headless SessionStart 스트리밍)였고, 유지된 노트 대부분은 다른 visible 노트가 "같은 계열"로 인용하는 anchor이거나(예: v2.1.222가 v2.1.211/212/214를 인용) 현행 동작을 서술 중이었다. 은퇴 2건이 바로 "인용 없는 서사만 은퇴됐다"는 판정 기준의 양성 사례다. **판정이 버전 번호가 아니라 인용 관계로 이루어졌다는 뜻**이며, **이 판정 기준을 같은 릴리즈에서 정식 조항으로 승격했다**(sauron FAIL 지적 → 같은 커밋 내 정합화 — 위 「은퇴 판정 기준」참조). 잔존 49건/11파일은 결함이 아니라 3조건 판정에 따른 유지 결과이므로, 다음 회고가 이를 "잔존 N건" 부채로 오인하지 않도록 여기 고정 기록한다.
128
+
113
129
  ### Cross-References
114
130
 
115
131
  R005(HTML-comment 컨텍스트 최적화), R023(Deprecated-Platform-Feature Staleness Check — 폐기 참조를 결정론적으로 탐지하여 은퇴 후보를 조기 발굴), Origin #1473.
@@ -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
 
@@ -35,6 +35,10 @@ oh-my-customcode uses an **advisory-first enforcement model**. Most rules are en
35
35
 
36
36
  > **v2.1.222+**: PreToolUse auto-allow 훅이 background agent task(summaries/compaction/renames)에서 tool restriction을 우회하던 문제가 수정되었습니다. 즉 위 Enforcement Tiers 표의 **Hard Block 계층(stage-blocker, dev-server tmux, rule-deletion-guard)이 background agent task 경로에서 우회될 수 있었다**는 뜻이며, background agent를 쓰는 장기 무인 루프에서 hard-block 훅이 실제로는 강제되지 않는 구간이 존재했습니다. v2.1.211/212/214 훅 결정 존중 체인의 연장선입니다.
37
37
 
38
+ > **v2.1.247+**: 훅 또는 background agent가 수 메가바이트의 error 출력을 찍어 대화를 overflow시켜 세션이 "Prompt is too long"으로 멈추던 결함이 수정되었습니다. 같은 릴리즈에서 hook/background task의 output file을 쓸 수 없을 때 무한 메모리 증가하던 문제도 수정되어, 이제 출력이 소실된 위치를 파일에 남깁니다. 이 저장소는 advisory 훅(`r007-r008-drift-advisor.sh`, `failure-ledger.sh`, `fail-axis-cause-advisor.sh` 등)을 다수 운용하므로, 훅 출력 폭주가 세션 자체를 wedge시키는 이 실패 클래스에 해당합니다 — v2.1.211/212/214/222 훅 신뢰성 계열의 연장선입니다.
39
+
40
+ > **v2.1.248+**: 훅 관측성이 두 건 강화되었습니다 — (a) `PermissionRequest`/`PreToolUse` 훅이 유효하지 않은 응답을 출력해 background session이 조용히 대기하던 결함이 수정되어, 이제 `claude agents` 행이 해당 훅 이름과 스키마 에러를 표시합니다. (b) 훅이 stdout으로 낸 `{…}` 객체가 유효한 JSON이 아닐 때 조용히 plain text로 처리하던 결함이 수정되어, 이제 parse 에러와 함께 훅 에러로 보고됩니다. 위 v2.1.214 "훅 stdout JSON이 스키마 검증에 실패할 때 exit code 2가 문서대로 차단하지 못하던 문제" 노트와 같은 계열 — 훅 실패가 무음에서 가시화되는 흐름의 연속입니다.
41
+
38
42
  ## Why Advisory-First
39
43
 
40
44
  1. **Agent flexibility**: Hard blocks can trap agents in unrecoverable states
@@ -50,7 +54,7 @@ If advisory enforcement proves insufficient for specific rules, these are candid
50
54
  | Rule | Candidate Hook | Status | Condition for Promotion |
51
55
  |------|---------------|--------|------------------------|
52
56
  | 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) |
57
+ | 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
58
 
55
59
  Promotion requires: (1) measured violation rate data, (2) user approval, (3) rollback plan.
56
60
 
@@ -334,21 +334,50 @@ Origin: #1563 찐빠 #3 — `gh issue edit --assignee`가 gh 2.86.0에 없는
334
334
  | Anti-pattern | Required |
335
335
  |--------------|----------|
336
336
  | 이전 턴에서 본 HEAD SHA를 위임서에 그대로 기재 | 위임 직전 `git rev-parse --short HEAD` 실측값 기재 |
337
+ | 브랜치 이름은 고정이라 보고 HEAD SHA만 재실측 | 브랜치 이름도 함께 재실측 — 공유 워크트리에서는 **브랜치 이름도 턴 단위 수명**이다(#1595 #1, R017 「게이트는 분기 시점 1회가 아니라 상태변경 위임마다」) |
337
338
 
338
339
  Origin: #1584 #5 (v1.1.45 세션 — 커밋 위임서에 `develop @ 96ef8f85`로 적었으나 실측은 `f6d3f518`; #1572 머지 후 pull 미반영). R017 「메모리 TODO를 위임 전제로 쓸 때」의 **세션 내 축소판** — 스냅샷의 수명이 세션 간이 아니라 **턴 간**이라는 차이만 있다.
339
340
 
341
+ #### 참인 전제 ≠ 참인 함의 — 브랜치 전환 위임 (Origin: #1595 #2)
342
+
343
+ uncommitted 변경이 있는 상태의 브랜치 전환을 위임할 때, 위임서에 **전환의 결과를 확정형으로 예측해 적지 않는다**. "대상 브랜치에 그 파일이 없다"는 **사실**에서 "전환해도 안전하다"는 **함의**는 도출되지 않는다 — 파일이 **없기 때문에** checkout이 그 파일을 삭제해야 하고, modified 상태면 거부된다.
344
+
345
+ 위임서에는 예측 대신 다음 두 가지를 적는다.
346
+
347
+ 1. **대상 브랜치와의 파일 집합 차이를 먼저 열거**한다 — `git status --short`(로컬 변경분) + 각 경로에 대해 `git cat-file -e <target>:<path>`(대상 브랜치 존재 여부). 결과는 **관측값**으로만 기재하고 전환 가능 여부를 단정하지 않는다.
348
+ 2. **표준 문구를 유지**한다 — "`stash`/`reset`/`clean`/force 일절 금지. 전환이 거부되면 **즉시 중단하고 오류 전문을 그대로 보고**하라." 부작용 없는 사전 확인 수단이 마땅치 않으므로, 이 금지 목록이 실질 방어선이다.
349
+
350
+ | Anti-pattern | Required |
351
+ |--------------|----------|
352
+ | "대상 브랜치에 없는 파일이므로 전환 후 untracked가 됩니다"처럼 전환 결과를 확정형으로 위임서에 기재 | 파일 집합 차이를 관측값으로만 열거; 결과 예측은 기재하지 않음 |
353
+ | 전환 거부 시 서브에이전트가 `stash`/`reset`/`clean`으로 자체 우회 | 위임서에 금지 목록 + "거부 시 즉시 중단, 오류 전문 보고"를 표준 문구로 포함 |
354
+
355
+ Origin: #1595 #2 (v1.1.48 세션 — `git checkout -b release/v1.1.48 develop`이 `tests/fixtures/agora/*.json` 6개 때문에 거부. `git cat-file -e develop:…` 실측은 "develop에 없음"으로 **참이었으나** 함의가 반대였다). **완화 실증**: 금지 목록이 작동해 mgr-gitnerd가 강제 전환을 시도하지 않았고 **손실 0**. Cross-ref: R020 Read-Before-Characterize, R001 Pre-Delegation Blast-Radius Enumeration.
356
+
340
357
  ### Parallel Delegation — Sibling-Agent Disclosure
341
358
 
342
359
  2개 이상의 서브에이전트를 같은 메시지에서 병렬 스폰할 때, 각 위임 프롬프트는 **형제 에이전트의 존재와 각자의 담당 범위**를 고지해야 한다. 서브에이전트는 격리된 컨텍스트에서 실행되어 형제를 인지할 수 없으므로, 고지가 없으면 `git status` 같은 **저장소 전역 공유 뷰**의 출력을 자기 변경분으로 오독하거나 경합 원인을 "외부 세션/프로세스"로 오귀속한다.
343
360
 
344
361
  고지에 포함할 것: 동시 실행 에이전트 수, 각 에이전트의 담당 파일/영역, 그리고 "공유 뷰에 타 에이전트 변경분이 함께 보이므로 **자기 담당 범위만 기준으로 보고**하라"는 지시.
345
362
 
363
+ **파일 소유권만으로는 부족하다 — 공유 자원도 고지 대상이다 (Origin: #1598).** 편집 대상 파일이 완전히 disjoint해도 형제 에이전트는 **검증 명령·CPU·`$TMPDIR`**을 공유한다. 위임서에 다음 셋을 함께 규정한다.
364
+
365
+ | 공유 자원 | 위임서에 규정할 것 |
366
+ |-----------|--------------------|
367
+ | 검증 명령 | 완료 조건에 **동일한 검증 명령**(`bun test` 등)이 들어가면 그 사실을 고지하거나, 검증을 오케스트레이터가 회수해 **직렬 1회**로 실행한다. 스위트가 저장소 tracked 파일을 이동·삭제·복구하면 동시 실행 시 한쪽이 다른 쪽의 픽스처를 지운다 |
368
+ | CPU | 초 단위 타임아웃 예산에 의존하는 테스트는 병렬 배치에서 제외하거나 그 예산을 고지한다 — CPU 포화 시 스텁조차 기동을 마치지 못한다 |
369
+ | `$TMPDIR` | 임시 파일을 쓰는 실험·계측은 **에이전트별 고유 경로**를 지정하고, "임시 파일 누수" 같은 측정은 그 격리 경로에서만 계수한다 |
370
+
346
371
  | Anti-pattern | Required |
347
372
  |--------------|----------|
348
373
  | 병렬 스폰 프롬프트에 형제 에이전트 고지 없이 위임 → 공유 뷰 출력을 오독하거나 원인을 "외부 프로세스"로 오귀속 | 각 프롬프트에 동시 실행 에이전트 수 + 각자 담당 범위 + "자기 담당 범위만 기준으로 보고" 지시 명시 |
374
+ | 파일 소유권만 고지하고 동일 검증 명령을 각 에이전트 완료 조건에 넣어 병렬 발주 | 검증 명령 공유를 고지하거나 검증을 오케스트레이터가 직렬 1회로 회수 |
375
+ | 공유 `$TMPDIR`에 고정 경로로 임시 파일을 쓰고 그 디렉토리를 전수 계수 | 에이전트별 고유 경로 사용 + 그 경로만 계수 |
349
376
 
350
377
  > Origin: #1518 (찐빠 #3 — 미고지 git 에이전트가 형제를 "외부 프로세스"로 오귀속; 같은 세션에서 고지한 4개 구현 에이전트는 전원 정확히 구분 보고 — 대조 실증). Cross-ref: R009 (병렬 실행 조건).
351
378
 
379
+ > Origin 보강: #1598 — 파일이 완전 disjoint한 병렬 배치에서 위양성 4종 발생(judge.sh 테스트 7건 ENOENT: 두 테스트가 tracked `verdict-schema.json`을 cp→rm→복구 / reviewers.sh 타임아웃 테스트 간헐 실패: CPU 포화 / "임시 파일 누수 1건" 오측정: 형제 잔여물, 격리 셔임 재측정 시 0). **3종의 원인은 오케스트레이터가 위임서에 넣은 완료 조건 자체였다** — 형제 고지의 결함이 아니라 고지 항목의 누락이다.
380
+
352
381
  #### 고지는 귀속 후보를 늘릴 뿐 증거 등급을 올리지 않는다
353
382
 
354
383
  형제 고지를 받았더라도 **정황 귀속(형제 탓)은 여전히 오답을 낸다** — 오히려 고지가 그럴듯한 오귀속 대상을 제공한다. 공유 뷰의 이상 징후는 형제 고지 여부와 무관하게 **개입 실험**(캐시 제거·복원, `bash -x` 추적, 변경 되돌려 재현)으로 귀속해야 한다.
@@ -359,6 +388,16 @@ Origin: #1584 #5 (v1.1.45 세션 — 커밋 위임서에 `develop @ 96ef8f85`로
359
388
 
360
389
  > Origin: #1574 (v1.1.44 세션 대조 실증 — 동일 고지를 받은 3개 병렬 에이전트 중 [1]은 `bun test` 11 fail을 "형제가 그 파일 편집 중"으로 정황 귀속해 오답, [2]/[3]은 개입 실험으로 정확히 귀속). Cross-ref: R020 (Read-Before-Characterize — 정황으로 특성화 금지).
361
390
 
391
+ ##### "플래키"는 원인이 아니다 (Origin: #1598)
392
+
393
+ 간헐 실패에 **"플래키"·"부하 의존"이라는 판정을 결론으로 쓰지 않는다** — 그것은 "재현 조건을 아직 못 찾았다"는 뜻이지 "원인이 무작위"라는 뜻이 아니다. 각 서브에이전트는 격리 컨텍스트라 **형제가 같은 스위트를 동시에 도는 것을 구조적으로 볼 수 없으므로**, 형제 경합이 원인인 실패에 대해 각자 합리적이지만 틀린 "부하 의존 플래키" 결론에 도달한다. 간헐 실패는 개입 실험(단독 재실행 / 격리 `$TMPDIR` 재측정 / 형제 완료 후 재현)으로 귀속하고, 귀속에 실패하면 **"원인 미귀속 — 재현 조건 미확보"로 보고**한다.
394
+
395
+ | Anti-pattern | Required |
396
+ |--------------|----------|
397
+ | 간헐 실패를 "플래키"·"부하 의존"으로 판정하고 종료 | 개입 실험으로 귀속; 실패 시 "원인 미귀속"으로 보고(무작위라 단정 금지) |
398
+
399
+ Origin: #1598 (형제 병렬 배치의 위양성 4종 중 3종이 각 에이전트에서 "부하 의존 플래키"로 결론났고, 실제 원인은 형제와의 검증 명령·CPU·`$TMPDIR` 경합이었다).
400
+
362
401
  ## Universal bypassPermissions
363
402
 
364
403
  > **This section is the canonical single source for the bypassPermissions requirement.** R002 (MUST-permissions.md) and R006 (MUST-agent-design.md) reference this section rather than repeating it.
@@ -449,6 +488,12 @@ Before spawning any agent:
449
488
 
450
489
  > **v2.1.232+**: interactive session의 **non-teammate 에이전트 스폰이 기본 background 실행**으로 바뀌었습니다(subagent forking 기본 활성화의 일부). 즉 Agent 도구 호출의 반환은 "작업 완료"가 아니라 **"백그라운드 착수"일 수 있으므로**, 오케스트레이터는 스폰 반환이나 완료 통지를 완료 근거로 삼지 않고 R020 ground-truth(`git status` / `grep` / 검증 스크립트)로 확인합니다 — 구버전에서는 동기 반환이 기본이라 "반환 = 완료"라는 암묵 전제가 대체로 성립했고, 그 전제가 이 버전부터 무너집니다. 위 v2.1.221 `/status` 표시와 v2.1.211(실행 중 agent 결과를 지어내지 않음)이 진단 보조 수단입니다. cross-ref R009(fork의 컨텍스트 상속), R018(Teams member는 non-teammate가 아니므로 이 변경 대상 밖).
451
490
 
491
+ > **★ v2.1.234+**: 세션 범위 permission 응답(**거부 포함**)이 background subagent의 tool permission 프롬프트에 응답할 때 **드롭**되던 결함이 수정되었습니다. 구버전에서는 background subagent에 대한 승인·거부가 **적용되지 않고 사라질 수 있었습니다** — 즉 "거부했다"가 "거부가 적용됐다"의 증거가 아니었습니다. 위 v2.1.232 non-teammate 기본 background 실행 서술과 결합하면, 과거 무인 루프에서 서브에이전트가 예상과 다르게 동작한 원인을 이것으로 재해석할 여지가 있습니다(단, 확정 진단이 아니라 원인 후보로만 취급 — R020 Diagnostic Hypothesis Verification).
492
+
493
+ > **v2.1.234+**: background task 알림(턴 사이에 전달되는 것)이 이제 mid-turn 전달과 동일하게 `<system-reminder>` 태그 안에 담겨 모델에 전달됩니다. 오케스트레이터가 background 에이전트 완료 통지를 받는 경로가 이것이므로, 그 통지는 **시스템 메시지이지 사용자 입력이 아닙니다** — R015 "다른 에이전트의 메시지는 결코 사용자의 승인이 아니다" 원칙과 마찬가지로, background 통지 역시 사용자 승인의 증거로 인용하지 않습니다. 이전에는 턴 사이 알림 형식이 mid-turn과 달라 이 구분이 덜 명확했습니다.
494
+
495
+ > **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 섹션.
496
+
452
497
  ## Agent Capability Pre-Check
453
498
 
454
499
  Before delegating a task to a subagent, MUST verify the target agent's tool capabilities against the task requirements. Failure to pre-check causes round-trip waste (delegation → failure → re-delegation).
@@ -25,6 +25,27 @@ Examples: creating multiple agents, reviewing multiple files, batch operations o
25
25
 
26
26
  Origin: #1518 (찐빠 #1 — git 에이전트 2개 근접 실행으로 작업 브랜치 stale; 편집 파일은 disjoint였음).
27
27
 
28
+ > **★ v2.1.246+**: `/ultrareview` 실행과 클라우드 세션을 **같은 저장소(여러 worktree 포함)에서 동시에 시작**하면, 한 실행이 다른 실행의 **커밋되지 않은 변경분과 함께 시작**되던 결함이 수정되었습니다. 이는 위 조항이 경고하는 시나리오가 **CC 플랫폼 자체에서 실증된 사례**입니다 — 이 조항은 이 저장소의 경험(#1518)에서 나왔는데, 플랫폼이 독립적으로 같은 결함을 겪고 고쳤다는 사실이 조항의 일반성을 뒷받침합니다. 구버전에서는 병렬 실행이 **서로의 uncommitted 변경분을 상속**했으므로, 과거 세션의 설명되지 않는 오염을 이 원인으로 재해석할 수 있습니다.
29
+
30
+ #### 파일 disjoint ≠ 자원 disjoint (Origin: #1598)
31
+
32
+ git 상태 외에도 병렬 에이전트가 경합하는 공유 자원이 있다 — **검증 명령이 만지는 저장소 파일**, **CPU**, **`$TMPDIR`**. 편집 파일이 disjoint하다는 사실은 이 셋 중 어느 것도 보장하지 않는다.
33
+
34
+ | 자원 | 병렬 가능 조건 |
35
+ |------|----------------|
36
+ | 검증 명령(`bun test` 등) | 스위트가 저장소 tracked 파일을 이동·삭제·복구하지 않고, 초 단위 타임아웃 예산에 의존하지 않을 때만. 아니면 오케스트레이터가 **직렬 1회**로 회수 |
37
+ | CPU | 타임아웃 예산이 초 단위인 테스트는 동시 실행 금지 — 포화 시 프로세스 기동만으로 예산을 넘긴다 |
38
+ | `$TMPDIR` | 에이전트별 고유 하위 경로를 쓸 때만. 고정 경로를 공유하면 "누수 N건" 같은 측정이 형제 잔여물을 계상한다 |
39
+
40
+ **테스트가 tracked 파일을 이동시키지 않는다**: `cp` → `rm` → `finally` 복구 패턴은 병렬 경합 위양성뿐 아니라 **프로세스 중단 시 tracked 파일이 사라진 채 남는다**. 픽스처는 고유 임시 디렉토리에 사본을 만들어 조작하고 원본은 읽기만 한다.
41
+
42
+ | Anti-pattern | Required |
43
+ |--------------|----------|
44
+ | 편집 파일이 disjoint하므로 각 에이전트 완료 조건에 동일 `bun test`를 넣어 병렬 발주 | 검증을 직렬 1회로 회수하거나, 공유를 고지하고 결과 해석에서 형제 경합을 먼저 배제 |
45
+ | 테스트가 실제 저장소 tracked 파일을 `cp`→`rm`→`finally` 복구 | 고유 임시 디렉토리에 사본을 만들어 조작 — 원본은 읽기 전용 |
46
+
47
+ Origin: #1598. Cross-ref: R010 「Parallel Delegation — Sibling-Agent Disclosure」(고지에 담을 내용), R023(Delegated Verification Floor).
48
+
28
49
  ## Agent Teams Gate (R018)
29
50
 
30
51
  > Before spawning 2+ parallel agents, evaluate Agent Teams eligibility.
@@ -108,7 +129,7 @@ Reference: #1320 (fix), #1321 (session 113 retrospective 찐빠 #1), `feedback_l
108
129
 
109
130
  > **v2.1.224+**: **세션당 200 subagent spawn cap이 제거**되어 장기 세션이 신규 에이전트를 거부하지 않습니다(동시성 제한과 depth 제한은 유지). 위 표의 "Max instances 5 concurrent"는 **동시성** 제한이므로 그대로 유효합니다 — 제거된 것은 세션 누적 총량 cap입니다. `/fsd` 등 장기 무인 루프에서 후반 반복의 스폰 실패를 더 이상 누적 cap으로 진단하지 않습니다.
110
131
 
111
- > **v2.1.232+**: subagent forking이 **기본 활성화**되어 `subagent_type: "fork"` 서브에이전트가 전체 대화와 prompt cache를 상속합니다. 위 표의 "Instance independence — Isolated context, no shared state"는 **fork에는 성립하지 않습니다** — fork는 격리된 병렬 인스턴스가 아니라 컨텍스트 사본이므로, 위 Detection Criteria의 독립성 전제로 병렬 배치를 설계할 때 fork를 일반 subagent와 동일하게 취급하지 않습니다(오케스트레이터 컨텍스트가 그대로 전달되므로 위임 프롬프트의 범위 서술이 유일한 경계가 아님). 구버전에서는 fork가 opt-in이라 이 상속이 예외 경로였습니다. 또한 interactive session의 **non-teammate 에이전트 스폰이 기본 background 실행**이므로 스폰 반환은 완료 신호가 아닙니다(R010/R020).
132
+ > **v2.1.232+**: subagent forking이 **기본 활성화**되어 `subagent_type: "fork"` 서브에이전트가 전체 대화와 prompt cache를 상속합니다. 위 표의 "Instance independence — Isolated context, no shared state"는 **fork에는 성립하지 않습니다** — fork는 격리된 병렬 인스턴스가 아니라 컨텍스트 사본이므로, 위 Detection Criteria의 독립성 전제로 병렬 배치를 설계할 때 fork를 일반 subagent와 동일하게 취급하지 않습니다(오케스트레이터 컨텍스트가 그대로 전달되므로 위임 프롬프트의 범위 서술이 유일한 경계가 아님). 구버전에서는 fork가 opt-in이라 이 상속이 예외 경로였습니다. 또한 interactive session의 **non-teammate 에이전트 스폰이 기본 background 실행**이므로 스폰 반환은 완료 신호가 아닙니다(R010/R020). **v2.1.246+**: 이미 fork되었거나 backgrounded된 세션에서 다시 `/fork`하면 빈 대화로 시작되던 결함이 수정되었습니다 — 구버전에서는 재fork 시 위 컨텍스트·prompt cache 상속조차 깨질 수 있었습니다.
112
133
 
113
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보다 낮을 수 있습니다.
114
135
 
@@ -118,6 +139,8 @@ Reference: #1320 (fix), #1321 (session 113 retrospective 찐빠 #1), `feedback_l
118
139
 
119
140
  Runtime detection and splitting of stalled parallel agents. Complements pre-execution parallelization.
120
141
 
142
+ > **cross-ref (v1.1.50 실측)**: 병렬 위임 중 일부가 침묵·중간 절단되면, 재촉·재분할 전에 R018 `maxTurns` partial 표시(v2.1.246)를 먼저 의심한다 — 20턴 한도 절단이 v1.1.50 세션에서 병렬 4건 중 3건에 실증됐다. 상세는 R018 (MUST-agent-teams.md) Member Completion Verification 섹션.
143
+
121
144
  See detection signals, splitting rules, and example via Read tool.
122
145
 
123
146
  <!-- DETAIL: Adaptive Parallel Splitting — Detection, Splitting Rules, Example
@@ -187,26 +210,56 @@ Single agent spawns do NOT use the `[N]` prefix.
187
210
 
188
211
  ## Narrative Announcement Format (Before Spawn)
189
212
 
190
- Use markdown list format (not inline comma-separated) for parallel dispatch announcements. See correct/incorrect examples via Read tool.
213
+ 병렬 dispatch 산문 announce는 **줄 시작에 대괄호 숫자가 오는 리터럴 형식**을 쓴다. 마크다운 리스트 마커(`- `)나 백틱을 그 앞에 붙이지 않는다 — R008 판정 정규식이 줄 시작의 대괄호 숫자를 요구하므로, 리스트 형식은 **규칙을 지킨 응답이 위반으로 계상**된다.
214
+
215
+ ```
216
+ [secretary][opus] → Spawning:
217
+ [1] mgr-updater:sonnet → Group A 룰 파일 갱신
218
+ [2] lang-typescript-expert:sonnet → Group B 테스트 보강
219
+ ```
220
+
221
+ | Anti-pattern | Required |
222
+ |--------------|----------|
223
+ | 리스트 마커나 백틱을 번호 앞에 붙임 | 줄 시작에 대괄호 숫자 — 앞에 공백 외 문자를 두지 않음 |
224
+ | 헤더에 콜론 생략 | Spawning 뒤에 **콜론 필수** |
225
+ | 화살표 없이 콜론만으로 연결 | 에이전트타입:모델 다음에 화살표 필수 |
226
+
227
+ **정규식 정합 (Origin: #1595 #5)**: 위 코드 블록의 형식은 `.claude/hooks/scripts/r007-r008-drift-advisor.sh`의 판정식과 1:1 대응한다. 화살표는 U+2192, ASCII 하이픈-부등호, U+2014-부등호 3종만 인식된다. 규칙 문구와 탐지기 정규식이 어긋나면 **규칙 준수 응답이 위반으로 계상되고, 그 계수를 근거로 다시 규칙을 고치는 악순환**이 생긴다. 형식을 바꿀 때는 advisor 정규식을 같은 커밋에서 갱신한다(R016 Rule Wiring Check). 이 형식은 위 「Display Format」 섹션과 동일하다 — 두 섹션이 서로 다른 형식을 요구하지 않도록 유지한다.
191
228
 
192
229
  <!-- DETAIL: Narrative Announcement Format (Before Spawn)
193
- When announcing a parallel dispatch in prose text (not the Agent tool call itself), use a markdown list rather than inline comma-separated description:
230
+ 산문 announce(Agent 도구 호출 자체가 아니라 그 앞의 텍스트)는 advisor 정규식과 리터럴로 일치해야 한다.
194
231
 
195
232
  ### Correct
196
233
 
234
+ ```
235
+ [secretary][opus] → Spawning:
236
+ [1] {agent-a}:{model} → {task-a}
237
+ [2] {agent-b}:{model} → {task-b}
238
+ ```
239
+
240
+ ### Incorrect — 리스트 마커/백틱을 번호 앞에 붙임 (spawn-item 미매칭)
241
+
197
242
  ```
198
243
  병렬 실행:
199
- - [1] {agent-a}: {task-a}
200
- - [2] {agent-b}: {task-b}
244
+ - [1] {agent-a}:{model} → {task-a}
245
+ - [2] {agent-b}:{model} → {task-b}
246
+ ```
247
+
248
+ ### Incorrect — 헤더 콜론 누락 (spawn-header 미매칭)
249
+
250
+ ```
251
+ [secretary][opus] → Spawning 2 agents
252
+ [1] {agent-a}:{model} → {task-a}
201
253
  ```
202
254
 
203
- ### Incorrect
255
+ ### Incorrect — 화살표 누락 (spawn-item 미매칭)
204
256
 
205
257
  ```
206
- 병렬 실행: [1] {agent-a}가 {task-a}, [2] {agent-b}가 {task-b}.
258
+ [secretary][opus] → Spawning:
259
+ [1] {agent-a}:{model}: {task-a}
207
260
  ```
208
261
 
209
- The list form mirrors the tool-call `[N]` prefix pattern and scales better to 3+ concurrent agents.
262
+ 세 Incorrect 변형 모두 advisor가 announce로 세지 못해, 규칙을 지킨 응답이 R008 위반으로 계상된다.
210
263
  -->
211
264
 
212
265
  ## Result Aggregation
@@ -94,6 +94,14 @@ Use a `"*"` deny rule in `settings.json` to enforce a deny-by-default posture, t
94
94
  >
95
95
  > **일반 교훈**: 단일 릴리즈의 플랫폼 권한 개선은 롤백될 수 있으므로 **항구적 보호막으로 간주하지 않습니다**. 스코프 규칙은 개선 이전 상태를 기준으로 설계하고 플랫폼 개선은 defense-in-depth로만 취급합니다(`feedback_platform_claim_staleness` 계열 — 플랫폼 주장의 시효성).
96
96
 
97
+ > **v2.1.251+**: Bash·경로 권한검사 우회 수정 5건이 한 릴리즈에서 함께 발견·수정되었습니다 — (a) 정수 셸 변수에 산술식을 대입하는 명령(`OPTIND=1/0`, `RANDOM=2+2`)을 auto-approve하던 결함, (b) 샌드박스 내 Bash 명령이 자기 output file을 리다이렉트·교체할 수 있던 결함, (c) 작업 디렉토리 내부 심링크가 permission check **이후** 교체(TOCTOU)되어 Read/Write/Edit가 승인 영역 밖을 접근할 수 있던 결함, (d) Grep/Glob이 심링크로 도달한 검색 경로에 `Read(...)` deny 규칙을 적용하지 못하던 결함, (e) Workflow tool이 permission check 실행 **전에** 세션이 읽을 수 없는 `scriptPath`를 먼저 읽고 에러에 그 경로를 그대로 인용하던 결함(cross-ref R023 Workflow Script Sanity Check). 한 릴리즈에서만 5건이 나왔다는 사실 자체가 위 「일반 교훈」— 단일 릴리즈의 플랫폼 권한 개선은 롤백될 수 있으므로 항구적 보호막으로 간주하지 않는다 — 를 강하게 재확인시킵니다.
98
+
99
+ > **v2.1.247~251 재도입 여부 확인 (실측)**: 위 「일반 교훈」이 언급하는 v2.1.233 롤백 2건(Windows Git Bash의 Cygwin-style symlink 우회, Bash 입력 리다이렉션 `< file`)의 "좁힌 형태 재도입"은 v2.1.247~251 CHANGELOG 범위에서 **확인되지 않았습니다** — "Cygwin"이라는 단어도 `< file` 입력 리다이렉션 언급도 4개 릴리즈 어디에도 없습니다. 대신 v2.1.251에 위 5건의 (c)(d)처럼 **메커니즘이 다른** 별개의 심링크·경로 우회 수정이 새로 등장했습니다 — 같은 "심링크 우회"라는 결과이지만 원인 버그는 다릅니다. 따라서 위 "233에서 롤백됨" 서술은 이 시점까지 **여전히 유효**하며, 재도입이 확인되면 이 노트를 갱신합니다.
100
+
101
+ > **v2.1.238+**: Bash 도구의 permission 검사가 zsh 전용 조건문(shell conditional) 문법에 대해 추가로 개선되었습니다. 이는 위 v2.1.221 "zsh `[[ ]]` 정규식 조건문 안에서 숨겨진 명령이 권한 검사를 우회"의 **직접 연장선**입니다 — "개선"으로만 기술되어 있어 v2.1.221 수정이 완전 해결이 아니었거나 추가 우회 벡터가 있었음을 시사합니다. 이 저장소의 Bash 도구 실행 셸이 zsh이므로(R005 #1540 실측) 직접 관련됩니다.
102
+
103
+ > **v2.1.246/248+**: (246) 끝에 매달린 `&&`/`||`가 있는 손상된(malformed) 명령에 대해 Bash 권한검사가 이제 **항상 승인을 요구**합니다 — 구버전에서는 이런 형태가 검사를 우회할 수 있었습니다. (248) `--restricted`(또는 `CLAUDE_CODE_RESTRICTED=1`) 모드가 신설되어 명령/코드 실행 도구와 `WebFetch`를 제거하고(`--tools`에 명시 시 예외), 파일 도구를 작업 디렉토리 내부로 제한하며, `bypassPermissions`를 거부하고, user/project/local settings 파일을 무시합니다. 이 저장소는 기본적으로 `bypassPermissions`를 쓰므로(R010 Universal bypassPermissions) `--restricted`와는 **상호 배타적**입니다 — 이 저장소 워크플로우에는 적용하지 않되, 신규 안전 모드 옵션으로 존재를 기록합니다.
104
+
97
105
  ### Todo/Task 도구 기본 제거 (v2.1.233+) — 위 표의 †
98
106
 
99
107
  CHANGELOG v2.1.233 원문: *"Todo/task-tracking tools (TaskCreate/Get/Update/List, TodoWrite) are no longer available on Opus 4.8, Sonnet 5, Fable 5, Mythos 5, and newer models; set `CLAUDE_CODE_ENABLE_TODO_TOOLS=1` to bring them back"*. 이 저장소 에이전트 **49개 중 46개**(`claude-sonnet-5` 41 + `claude-opus-5` 5)가 대상 모델이므로 실행 환경의 기본값은 **부재**다(잔여 3개는 `haiku`).
@@ -35,6 +35,12 @@ The following git commands have caused working tree loss in past sessions (#1146
35
35
 
36
36
  > **v2.1.221/222+**: v2.1.222에서 worktree-isolated 세션과 그 subagent가 main checkout에 대해 파괴적 git 명령을 실행할 수 있던 문제가 수정되어, isolation이 모든 세션 타입의 file edit과 Bash에 적용됩니다. v2.1.221에서는 `/fork` 세션이 원본 세션 checkout이 아니라 자체 worktree를 생성하도록 변경되었습니다. **완화 아님**: 위 Destructive Git Commands 표의 per-invocation 승인 요구와 아래 Pre-Delegation Blast-Radius Enumeration은 그대로 유지됩니다. 플랫폼 isolation은 격리 경계를 강화할 뿐, 사용자가 판단하는 데 필요한 blast-radius 열거를 대체하지 않습니다(v2.1.183/208 플랫폼 블록과 동일한 defense-in-depth 관계).
37
37
 
38
+ > **v2.1.234/236/238+**: 승인 다이얼로그 표시 무결성 결함이 3개 릴리즈 연속으로 발견·수정되었습니다. (234) permission 프롬프트 comment 필드에서 Shift+Tab을 누르면 필드를 닫는 대신 **edit을 승인하고 세션 전체 edit 권한을 부여**하던 결함. (236) managed-settings 승인 프롬프트가 **표시되지 않으면서 첫 키입력을 승인으로 소비**하던 결함 — 프롬프트를 보지 못한 사용자의 무관한 입력이 승인으로 처리될 수 있었습니다. (238) 대화상자 표시 텍스트와 "don't ask again" 옵션이 이제 항상 실제 승인 범위와 일치하도록 개선되고, 내용이 완전히 표시될 수 없으면 "don't ask again"이 보류됩니다. 이 3건은 위 v2.1.223 "탭·비가시 유니코드 패딩 명령이 자기 일부를 숨김" 결함과 **같은 계열의 반복**이며, 단발 결함이 아니라 승인 다이얼로그 표시 무결성이 여러 릴리즈에 걸쳐 계속 발견되고 있는 구조적 계열임을 실증합니다. Pre-Delegation Blast-Radius Enumeration(모델이 파괴 대상을 별도 열거)이 플랫폼 다이얼로그로 대체될 수 없다는 원칙은 이 반복으로 더욱 강화됩니다.
39
+
40
+ > **v2.1.236+**: macOS 샌드박스에서 wildcard read-deny 규칙(예: `**/.env`)이 이제 허용된 read 영역 **내부에서도 우선 적용**되고, 매칭된 디렉토리의 콘텐츠까지 커버하며, 파일명 변경으로 우회할 수 없습니다. 위 v2.1.224 "sandbox filesystem deny 항목의 후행 슬래시가 조용히 우회 가능하던 결함"과 같은 sandbox deny 규칙 우회 계열의 추가 하드닝입니다 — 구버전에서는 read-deny 와일드카드가 허용 영역 안에서 무력화되거나 파일명 rename으로 우회될 수 있었습니다.
41
+
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
+
38
44
  ### Pre-Delegation Blast-Radius Enumeration
39
45
 
40
46
  > 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.