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.
- package/README.md +7 -6
- package/dist/cli/index.js +171 -294
- package/dist/index.js +65 -65
- package/package.json +2 -2
- package/templates/.claude/agents/agora-runner.md +114 -0
- package/templates/.claude/hooks/hooks.json +10 -0
- package/templates/.claude/hooks/scripts/agent-teams-advisor.sh +6 -1
- package/templates/.claude/hooks/scripts/claude-md-reinject.sh +65 -0
- 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 +7 -1
- package/templates/.claude/rules/MUST-agent-design.md +15 -4
- package/templates/.claude/rules/MUST-agent-teams.md +19 -0
- package/templates/.claude/rules/MUST-completion-verification.md +53 -0
- package/templates/.claude/rules/MUST-continuous-improvement.md +20 -4
- package/templates/.claude/rules/MUST-enforcement-policy.md +7 -3
- package/templates/.claude/rules/MUST-orchestrator-coordination.md +45 -0
- package/templates/.claude/rules/MUST-parallel-execution.md +61 -8
- package/templates/.claude/rules/MUST-permissions.md +8 -0
- package/templates/.claude/rules/MUST-safety.md +6 -0
- package/templates/.claude/rules/MUST-sync-verification.md +21 -0
- package/templates/.claude/rules/MUST-tool-identification.md +22 -4
- package/templates/.claude/rules/SHOULD-ecomode.md +4 -0
- package/templates/.claude/rules/SHOULD-error-handling.md +2 -0
- package/templates/.claude/rules/SHOULD-interaction.md +4 -0
- package/templates/.claude/rules/SHOULD-memory-integration.md +2 -0
- package/templates/.claude/rules/SHOULD-verification-ladder.md +2 -0
- 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 +21 -3
- 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/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 +21 -3
|
@@ -158,6 +158,20 @@ Origin: #1492 (Session 132) — cc-release-monitor 워크플로우 삭제(#1454,
|
|
|
158
158
|
|--------------|----------|
|
|
159
159
|
| 원격 머지 후 stale 로컬 develop에서 릴리즈 브랜치 분기 | 분기 전 `git pull origin develop`; PR 생성 후 mergeStateStatus 확인 — CONFLICTING이면 `git merge origin/develop`+both-유지 해결 후 재CI |
|
|
160
160
|
|
|
161
|
+
### 게이트는 분기 시점 1회가 아니라 상태변경 위임마다 (Origin: #1595 #1)
|
|
162
|
+
|
|
163
|
+
위 게이트는 "브랜치 **분기 전** pull"을 규정하지만, 공유 워크트리에서는 **세션 도중 다른 행위자가 브랜치 자체를 바꾼다**. 따라서 git 상태를 바꾸는 위임(브랜치 생성·전환, 커밋, 머지, push) **직전마다** 브랜치 이름과 HEAD SHA를 재실측하고 세션 초반 값과 대조한다 — `git rev-parse --abbrev-ref HEAD` 와 `git rev-parse --short HEAD` 두 줄이면 충분하다.
|
|
164
|
+
|
|
165
|
+
값이 달라졌으면 위임을 중단하고 **원인을 먼저 실측**한다(`git reflog`로 전환·커밋 주체와 시각 확인). #1584 #5가 "SHA의 수명은 턴 단위"를 규정했는데, 공유 워크트리에서는 **브랜치 이름조차 턴 단위 수명**이다.
|
|
166
|
+
|
|
167
|
+
| Anti-pattern | Required |
|
|
168
|
+
|--------------|----------|
|
|
169
|
+
| 세션 초반에 실측한 브랜치·HEAD를 세션 내내 유효한 사실로 사용 | 상태변경 위임 직전마다 브랜치 이름 + HEAD SHA 재실측·대조 |
|
|
170
|
+
| 파일 목록·`git ls-files` 결과를 "확인된 사실"로 저장하고 재확인 트리거 없이 재사용 | 실측값에 **측정 시각**을 함께 기록하고, 위임 전제로 쓰기 전 재실측 |
|
|
171
|
+
| 대조 불일치를 발견하고도 원인 규명 없이 위임 강행 | `git reflog`로 전환·커밋 주체와 시각을 실측한 뒤 재계획 |
|
|
172
|
+
|
|
173
|
+
Origin: #1595 #1 (v1.1.48 세션 — 세션 시작 시 `develop @ 1b4973d5` 실측 후 진행했으나 다른 세션이 14:56·15:10에 `feat/agora-anonymous-consensus`를 만들고 커밋 2개를 쌓았고, wiki 재동기화 Phase 2까지 미탐지. 같은 세션에서 `git ls-files tests/fixtures/agora/`가 초반 0건 → 후반 6건으로 바뀌었다). Cross-ref: R010 「저장소 상태 기재도 같은 규율」(위임서 기재 각도), R011(Temporal Decay).
|
|
174
|
+
|
|
161
175
|
## Pre-Release Target Version Ground-Truth Gate (Origin: #1457)
|
|
162
176
|
|
|
163
177
|
새 릴리즈의 target 버전을 선정하거나 구현/구현-위임 프롬프트에 target 버전을 전달하기 전, 반드시 원격 실측으로 다음 버전을 확정한다: `git tag --sort=-v:refname | head -1`(최신 태그) + `npm view <pkg> version`(배포된 최신)의 **max에 patch를 더한 값**을 target으로 삼는다. 세션 메모리의 버전 스냅샷(예: "npm latest 1.1.6")은 **참고용이며 ground-truth가 아니다** — 직전 세션에서 릴리즈가 진행돼 stale일 수 있다. stale 버전으로 위임하면 이미 배포된 버전을 target으로 잡아 milestone-closed STOP에 걸리고 재타겟팅 왕복이 강제된다.
|
|
@@ -179,6 +193,11 @@ Origin: #1457 (Session 128 회고 찐빠 #1) — 오케스트레이터가 stale
|
|
|
179
193
|
|
|
180
194
|
Origin: #1574 (v1.1.44 세션 — mgr-sauron 브리핑의 "선재 항목" 4건 중 3건이 부정확: 이미 해소된 항목, 의도적 차이를 결함으로 오인, 규모 과대). **완화 요인**: 프롬프트에 "그대로 믿지 말고 직접 확인하라"를 명시해 3건 전부 에이전트가 정정 — #1443의 "실측값 기준으로 동기화하라" 방어선과 동일 효과. Cross-ref: R011(메모리 신뢰도·Temporal Decay), R020(Diagnostic Hypothesis Verification).
|
|
181
195
|
|
|
196
|
+
**추가 사례 2건 (Origin: #1601, v1.1.49 세션)**:
|
|
197
|
+
|
|
198
|
+
- **버전 판정**: 세션 메모리에 "agora 정식 마이너 릴리즈 예정"이 기록돼 있었고, 오케스트레이터가 이를 semver 판정(minor vs patch) 근거로 인용했다가 사용자 정정("스킬 하나 추가한다고 마이너패치를?")을 받았다. 메모리 스냅샷은 판정을 정당화하지 못한다 — **직전 세션도 같은 오독을 했을 수 있기 때문**이다.
|
|
199
|
+
- **테스트 baseline**: verify-build 단계에서 baseline을 메모리의 "138 pass"로 삼았으나 실측은 **2291 pass / 0 fail**이었다(138은 부분집합에 불과했다). 이번엔 fail이 0이라 판정에 영향이 없었으나, **fail이 0이 아니었다면 잘못된 baseline이 회귀를 통과시켰을 것**이다. baseline은 메모리 기록이 아니라 **전체 실행 실측값**으로 잡는다.
|
|
200
|
+
|
|
182
201
|
## CC 버전 노트 반영 전 — 스코프 상한 이후 릴리즈 확인 (Origin: #1584 #1)
|
|
183
202
|
|
|
184
203
|
CC 버전 노트를 룰에 반영하기 **전**, `npm view @anthropic-ai/claude-code version` + `claude --version`을 실측해 **스코프 상한 버전 이후의 릴리즈 존재 여부**를 확인한다. 있으면 그 CHANGELOG를 먼저 읽어 **롤백·후속 변경**을 파악한 뒤 반영한다. 이슈 생성과 작업 사이의 간극 동안 플랫폼이 스스로 뒤집을 수 있으므로 — **이슈 번호는 최신 릴리즈를 의미하지 않는다**.
|
|
@@ -204,6 +223,8 @@ R017 게이트(mgr-sauron) 통과 선언 후 신규 결함 발견 등으로 스
|
|
|
204
223
|
|
|
205
224
|
Origin: #1584 #4 (v1.1.45 세션) — R021 자기 서술 staleness 반영을 R017 통과 **후** 수행해 위키 재동기화 1회 + 게이트 전량 재실행 발생. 포함 판단 자체는 옳았고 **시점**이 결함이었다.
|
|
206
225
|
|
|
226
|
+
> **v2.1.233+**: `claude plugin validate`가 **bare `.claude/skills` 디렉토리**(플러그인 매니페스트 없는 스킬 트리)도 검사해, frontmatter 파싱에 실패하는 `SKILL.md`를 보고합니다. 이 저장소의 `.claude/skills/**/SKILL.md`는 아래 Quick Verification Commands가 **개수만** 세고 frontmatter 유효성은 세지 않으므로, 스킬 추가·수정 후 `claude plugin validate`를 개수 대조와 **함께** 실행해 파싱 실패를 결정론적으로 잡습니다(구버전에서는 이 경로가 검사 대상이 아니어서 깨진 frontmatter가 런타임 미로드로만 드러났습니다). Cross-ref: R023(Tier 1 결정론적 검증).
|
|
227
|
+
|
|
207
228
|
## Quick Verification Commands — agent/skill/guide/wiki counts via ls/find/wc. See commands via Read tool.
|
|
208
229
|
|
|
209
230
|
<!-- DETAIL: Quick Verification Commands
|
|
@@ -37,7 +37,7 @@ R008 prefix(announce)와 실제 도구 호출은 분리된 단계다. prefix 를
|
|
|
37
37
|
|
|
38
38
|
| Anti-pattern | Required |
|
|
39
39
|
|--------------|----------|
|
|
40
|
-
|
|
|
40
|
+
| AskUserQuestion 호출 앞에 Core Rule 형식의 prefix 라인(에이전트·모델 대괄호 다음 화살표와 Tool 표기)만 출력하고 `questions` 파라미터 없이/빈 배열로 호출 | prefix + `questions` 배열(최소 1개) 모두 채워 호출 |
|
|
41
41
|
| announce 후 payload 의 required 필드 누락 (announce-payload separation gap) | announce 와 동일 메시지에서 required 필드 완비 호출 |
|
|
42
42
|
|
|
43
43
|
Cross-reference: R020 (action-completeness precondition — invoke 전에 required 파라미터 확인). Reference issue: #1324 (찐빠: AskUserQuestion `questions`-missing recurrence).
|
|
@@ -92,17 +92,35 @@ matches the spawn announcement:
|
|
|
92
92
|
[2] lang-python-expert:sonnet → Python code review
|
|
93
93
|
```
|
|
94
94
|
|
|
95
|
+
### Spawn Announce 리터럴 — advisor 정규식 정합 (Origin: #1595 #5)
|
|
96
|
+
|
|
97
|
+
위 예시는 `.claude/hooks/scripts/r007-r008-drift-advisor.sh`의 판정식과 리터럴로 일치한다. 다음 변형은 **미매칭**되어, 규칙을 지킨 응답이 R008 위반으로 계상된다.
|
|
98
|
+
|
|
99
|
+
| 금지 변형 | 미매칭 이유 |
|
|
100
|
+
|-----------|-------------|
|
|
101
|
+
| 번호 앞에 리스트 마커(`- `)나 백틱을 붙임 | spawn-item 정규식은 **줄 시작의 대괄호 숫자**를 요구하며, 선행 공백만 허용한다 |
|
|
102
|
+
| Spawning 뒤 콜론 생략 (예: "Spawning 4 agents") | 헤더 정규식이 **콜론**을 요구한다 |
|
|
103
|
+
| 에이전트타입:모델 뒤에 화살표 없이 설명만 이어붙임 | spawn-item 정규식이 **화살표**를 요구한다 (U+2192 / ASCII 하이픈-부등호 / U+2014-부등호 3종만 인식) |
|
|
104
|
+
|
|
105
|
+
규칙 문구와 탐지기 정규식이 어긋나면 오탐 계수가 다시 규칙 개정의 근거가 되는 악순환이 생긴다. 형식을 바꿀 때는 advisor 정규식을 같은 커밋에서 갱신한다(R016 Rule Wiring Check).
|
|
106
|
+
|
|
107
|
+
**문서 작성 주의**: advisor의 announce 정규식에는 줄 시작 앵커가 없어, 표 셀·인라인 백틱 안에 완전한 리터럴을 넣으면 **그 문서를 인용하는 응답 턴이 announce로 오계상**된다(482턴 실측에서 실제 발생). 형식 예시는 코드 펜스 안에 줄 시작으로만 두고, 표에서는 산문으로 서술한다.
|
|
108
|
+
|
|
109
|
+
Cross-ref: R009 「Narrative Announcement Format」(같은 리터럴을 산문 announce에 적용), R020 「자가 계수는 advisor 판정식을 재현한다」.
|
|
110
|
+
|
|
111
|
+
Origin: #1595 #5 (v1.1.48 세션 — R008 위반 3건이 단일 턴에 집중. tool_use=5 / announce=2로 계산됐고, 실제 announce는 리스트 마커와 백틱이 앞에 붙은 형식이라 전부 미매칭. 헤더도 콜론이 없었다).
|
|
112
|
+
|
|
95
113
|
<!--
|
|
96
114
|
> **v2.1.174+**: Fixed the Workflow tool's `agent()` subagents missing per-agent attribution headers. Workflow-spawned subagents now carry attribution consistent with R008 — when authoring Workflow scripts, each `agent()` call is attributed like a direct Agent tool spawn. Align Workflow orchestration with the R008 `[agent][model] → Tool:` identification discipline: a Workflow `agent()` fan-out should still be reasoned about with the same per-agent identification model as parallel Agent tool spawns.
|
|
97
115
|
-->
|
|
98
116
|
|
|
99
117
|
## Tier-3 Interaction Tool Prefix (MANDATORY)
|
|
100
118
|
|
|
101
|
-
R008 "every tool call" applies to Tier-3 interaction tools too — NOT only file/exec tools. Applying the
|
|
119
|
+
R008 "every tool call" applies to Tier-3 interaction tools too — NOT only file/exec tools. Applying the Core Rule prefix form (에이전트·모델 대괄호 다음 화살표와 Tool 표기) to Agent/Bash/Read while omitting it on `AskUserQuestion`, `TodoWrite`, `EnterPlanMode`, etc. is a violation.
|
|
102
120
|
|
|
103
121
|
| Tool | R008 prefix required? |
|
|
104
122
|
|------|----------------------|
|
|
105
|
-
| AskUserQuestion | YES —
|
|
123
|
+
| AskUserQuestion | YES — Core Rule 형식의 prefix(에이전트·모델 대괄호 + 화살표 + Tool 표기 + 도구명)를 호출 앞에 출력 |
|
|
106
124
|
| TodoWrite | YES |
|
|
107
125
|
| EnterPlanMode / ExitPlanMode | YES |
|
|
108
126
|
| Skill | NO separate R008 prefix — identified via R007 `claude → {skill-name}` integrated header instead |
|
|
@@ -128,7 +146,7 @@ Agent(description: "[2] Python code review", subagent_type: "lang-python-expert"
|
|
|
128
146
|
|
|
129
147
|
매 도구 호출 직전, 이전 호출이 prefix 를 가졌는지에 의존하지 말고 다시 자가 점검:
|
|
130
148
|
|
|
131
|
-
1. 이 호출 위에
|
|
149
|
+
1. 이 호출 위에 Core Rule 형식의 prefix 라인(에이전트명·모델 대괄호 + 화살표 + Tool 표기 + 도구명)이 있는가?
|
|
132
150
|
2. agent-name 과 model 이 현재 컨텍스트와 일치하는가?
|
|
133
151
|
3. 이 호출에 도구 스키마상 required 파라미터가 모두 채워져 있는가? (예: AskUserQuestion 는 `questions` 배열이 비어 있지 않아야 함) prefix(announce)만 출력하고 실제 호출 payload 의 required 필드를 누락하면 안 된다.
|
|
134
152
|
|
|
@@ -86,8 +86,12 @@ Active removal of irrelevant retrieved content from agent context. Complements o
|
|
|
86
86
|
|
|
87
87
|
## Context Budget Management — Task-type-aware thresholds (research 40%, implementation 50%, review 60%, management 70%, general 80%). See full spec via Read tool.
|
|
88
88
|
|
|
89
|
+
> **v2.1.238+**: 장시간 대화형 세션에서 subagent tool 결과가 최근 표시 윈도우를 벗어나면 이제 메모리에서 해제되는 unbounded memory growth 결함이 수정되었습니다. 구버전 장기 세션(`/fsd` 등 자율 루프 포함)에서는 subagent tool 결과가 무한 누적되어 컨텍스트/메모리 예산 관리가 왜곡될 수 있었습니다 — 위 Context Budget Management의 task-type 임계값 계산이 이 결함으로 인해 부정확했을 가능성을 회고적으로 시사합니다.
|
|
90
|
+
|
|
89
91
|
> **v2.1.223+**: `CLAUDE_CODE_DISABLE_1M_CONTEXT`가 native 1M 창을 가진 **모든** Claude 모델을 auto-compaction으로 200K에 유지하도록 확대되었고(이전에는 고정 모델 목록), 미인식 model ID도 가정 컨텍스트 창 내로 유지됩니다(`CLAUDE_CODE_DISABLE_UNKNOWN_MODEL_WINDOW_ENFORCEMENT=1`로 복원). 위 임계값은 **창 대비 백분율**이므로 절대 토큰량은 이 env 설정에 따라 5배까지 달라집니다 — 1M 전제로 예산을 잡기 전 env 설정 여부를 확인합니다(cross-ref R006).
|
|
90
92
|
|
|
93
|
+
> **v2.1.251+**: Sonnet 5의 기본 auto-compact 창이 **전체 1M 컨텍스트로 변경**되어, 1M 창 세션이 이제 ~934K가 아니라 ~967K 토큰에서 auto-compact됩니다. 위 `CLAUDE_CODE_DISABLE_1M_CONTEXT` 노트와 **직접 상호작용**합니다 — 그 env가 **설정된 환경**에서는 여전히 200K로 강제 유지되지만, **비활성 환경**(기본값)에서는 이번 변경으로 실효 auto-compact 임계값 자체가 상향됩니다. 이 저장소 에이전트 다수가 `claude-sonnet-5`이므로, 위 임계값 표의 백분율 계산은 env 설정 여부에 더해 이 CC 버전 여부까지 함께 확인해야 절대 토큰량이 정확합니다.
|
|
94
|
+
|
|
91
95
|
<!-- DETAIL: Context Budget Management
|
|
92
96
|
|
|
93
97
|
Task-type-aware context thresholds that trigger ecomode earlier for context-heavy operations.
|
|
@@ -25,6 +25,8 @@ Recommended: {action1}, {action2}
|
|
|
25
25
|
| Retryable | Retry up to 3x with backoff (1s, 2s, 4s), then report |
|
|
26
26
|
| Non-recoverable | Save state, rollback if possible, detailed report, wait for user |
|
|
27
27
|
|
|
28
|
+
> **v2.1.246+**: 비대화형 세션(`-p`, SDK, 클라우드 세션)에서 서버 오류·연결 끊김·정체로 중간에 끊긴 응답을 이제 **자동으로 이어서 완료**한다 — 이전에는 오류로 종료됐다. `/fsd` 등 무인 자율 루프의 신뢰성에 직접 영향 — 구버전에서는 네트워크 일시 장애가 자율 루프 전체를 조기 종료시킬 수 있었다. 위 표의 "Retryable" 재시도 전략이 이제 이 경로에서는 플랫폼이 자동 수행하므로, 무인 루프 중단을 관측했을 때 "재시도 로직 부재"로 오진하기 전에 이 자동-이어짐 경계(오류 종류·세션 종류)를 먼저 확인한다.
|
|
29
|
+
|
|
28
30
|
## Validation
|
|
29
31
|
|
|
30
32
|
| When | Checks |
|
|
@@ -51,6 +51,8 @@
|
|
|
51
51
|
3. Agent effort level → Map to corresponding style
|
|
52
52
|
4. Default → `balanced`
|
|
53
53
|
|
|
54
|
+
> **v2.1.237+**: CC 내장 "Concise" output style이 추가됐다(`/config` > Output style에서 선택) — 결과를 먼저 제시하고 preamble·narration을 생략하며 작업 철저성은 유지한다. 위 표의 프로젝트-정의 `concise` 스타일과 이름이 겹치지만 별개다 — 이 저장소는 `korean-engineer` 커스텀 output style을 상시 활성화하므로(세션 레벨), 내장 "Concise"를 별도로 선택하지 않는다.
|
|
55
|
+
|
|
54
56
|
### Style Examples — See concise/balanced/explanatory examples via Read tool.
|
|
55
57
|
|
|
56
58
|
<!-- DETAIL: Style Examples
|
|
@@ -91,6 +93,8 @@ Trade-offs: RS256 is ~10x slower than HS256 but enables asymmetric key managemen
|
|
|
91
93
|
|
|
92
94
|
기본 활성화 스타일: `korean-engineer` (`.claude/output-styles/korean-engineer.md`).
|
|
93
95
|
|
|
96
|
+
> **v2.1.238+**: custom/project/plugin output style이 **세션 도중 default voice로 드리프트**하던 결함이 수정됐다. 이 저장소는 `korean-engineer` 커스텀 output style을 상시 활성화하므로 직접 해당한다 — 세션이 길어지면 격식체(R000 합쇼체)가 조용히 기본 voice로 돌아갈 수 있었다는 뜻이다. **회고적 함의**: 구버전에서는 세션 길이에 비례해 스타일 드리프트 위험이 있었으므로, 과거 세션에서 관측된 R000 합쇼체 이탈(feedback_honorific_register_drift 등)을 전부 모델의 규칙 위반으로만 귀속할 수 없다 — 일부 구간은 플랫폼 드리프트가 원인이었을 수 있다. **다만 이것을 면책으로 쓰지 말 것**: R000 준수 의무는 그대로이며, 이는 과거 관측의 재해석 근거일 뿐 향후 이탈을 정당화하지 않는다.
|
|
97
|
+
|
|
94
98
|
## Unverifiable External Product UI
|
|
95
99
|
|
|
96
100
|
> Origin: #1266 ② (High) — described Cloudflare Access "Add an application" Subdomain/Domain/Path fields as fact; the user found no such fields ("어디에도 없다").
|
|
@@ -484,6 +484,8 @@ References: #1226 (item 3), #1227.
|
|
|
484
484
|
|
|
485
485
|
> **v2.1.228+**: **session cleanup이 프로젝트 memory 폴더 내부 내용을 삭제하던 결함**이 수정되었습니다. 구버전에서는 MEMORY.md·archive 파일이 세션 정리 단계에서 소실될 수 있었으므로, 메모리 누락을 위 Failure Policy의 쓰기 실패로만 진단하지 않습니다 — 쓰기는 성공했으나 정리에 삭제된 경우일 수 있습니다. 위 Memory Scopes 표대로 `project` 스코프(`.claude/agent-memory/`)는 git tracked라 복구 가능하지만 `user`/`local` 스코프는 복구 수단이 없습니다.
|
|
486
486
|
|
|
487
|
+
> **v2.1.251+**: 디렉토리 변경으로 세션이 동일 ID의 기존 트랜스크립트 위에 재배치돼 트랜스크립트가 손상/유실되던 결함이 수정되었습니다. 위 v2.1.228 "session cleanup이 project memory 폴더 내용을 삭제하던 결함"과 **같은 계열의 데이터 무결성 보강**입니다 — 구버전에서는 트랜스크립트 자체가 손상될 수 있었으므로, 그 시기 세션의 R020 회고적 위반 계수(트랜스크립트 파싱 기반)가 **불완전한 원본을 셌을 가능성**이 있습니다. `/cd`로 디렉토리를 옮기는 워크플로우에서 특히 유의합니다.
|
|
488
|
+
|
|
487
489
|
> **v2.1.232+**: Cowork 세션이 **user-scope 메모리 파일의 외부 @-import를 인라인하지 않습니다**. 즉 `~/.claude/agent-memory/`의 MEMORY.md가 @-import로 외부 파일을 끌어오는 구조라면 세션 종류에 따라 그 내용이 컨텍스트에 없을 수 있으므로, 항상 로드되어야 하는 내용은 import 참조가 아니라 **MEMORY.md 본문**에 둡니다(위 200줄 예산 내 Hot/Warm 배치 원칙과 정합).
|
|
488
490
|
|
|
489
491
|
<!-- RETIRED (은퇴 릴리즈 v1.1.45, 보존 기준 v2.1.212 미만): > **v2.1.210+**: MEMORY.md 인덱스가 read limit을 초과하게 만드는 memory write는 이제 silent truncation 대신 명시적 오류를 반환합니다. write 실패는 여전히 non-blocking이지만, 오류 수신 시 log-warning으로 끝내지 말고 예산 초과 처리(Attention-Weight Tiering — Cold 항목 archive 이동)로 축소 후 재시도합니다 — 이전의 silent truncation을 가정하고 oversize write를 던지면 업데이트가 반영되지 않습니다. -->
|
|
@@ -181,6 +181,8 @@ Origin: #1438 (Session 125 회고 찐빠 #2) — fix Workflow의 verify 프롬
|
|
|
181
181
|
#### Common Violation (#1512)
|
|
182
182
|
Origin: #1512 (v1.1.27 세션 회고 찐빠 #2) — Workflow `args`를 JSON 객체로 전달했으나 하니스가 문자열로 인코딩해 `args.paths`가 undefined → 0 agents 실행으로 즉시 런타임 실패. `typeof args === 'string' ? JSON.parse(args) : args` 방어 추가 후 재실행 성공.
|
|
183
183
|
|
|
184
|
+
> **v2.1.246+**: MCP 도구 파라미터 스키마가 빈 객체(`{}`)일 때 인자가 실제 타입이 아니라 **JSON 문자열로 전달**되던 결함이 수정되었습니다. 이는 위 표의 "하니스가 객체 `args`를 문자열로 인코딩해 전달"(#1512) 버그와 **동일한 버그 클래스**가 MCP 도구 호출 경로에도 있었음을 확인시켜줍니다 — 구버전에서 스키마가 `{}`인 MCP 도구를 호출하면 인자가 문자열로 도착해 `args.<field>`가 undefined로 즉시 실패할 수 있었습니다. `typeof args === 'string' ? JSON.parse(args) : args` 방어는 Workflow 스크립트뿐 아니라 빈 스키마 MCP 도구를 감싸는 코드에도 같은 논리로 필요했다는 뜻입니다.
|
|
185
|
+
|
|
184
186
|
### Verifier Ground-Truth for Cross-Cutting Facts
|
|
185
187
|
|
|
186
188
|
Cross-cutting facts not verifiable from the primary source (external URLs, in-cluster DNS/hostnames, infra topology) MUST be supplied to the verifier as explicit ground-truth. Otherwise an adversarial verifier cannot distinguish a hallucinated value from a correct one — a verification blind spot.
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: agora
|
|
3
|
+
description: Use when a design or decision needs adversarial multi-round scrutiny from several independent model vendors. Runs anonymized A/B/C reviewer rounds with a rotating judge and produces a consensus report.
|
|
4
|
+
scope: core
|
|
5
|
+
version: 1.0.0
|
|
6
|
+
user-invocable: true
|
|
7
|
+
argument-hint: "<topic> [--attach <path>] [--max-rounds <N>] [--auto]"
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Agora
|
|
11
|
+
|
|
12
|
+
## 개요
|
|
13
|
+
|
|
14
|
+
`agora`는 하나의 설계·결정 주제를 3개 독립 벤더 CLI에게 익명 A/B/C 라벨로 검토시키고, 라운드마다 모델이 바뀌는 심판이 판정하는 다중턴 합의 스킬입니다. 라벨-벤더 매핑은 `SEALED/`에 격리되어 심판의 입력 경로에 오르지 않으며, 벤더 공개는 최종 보고서 생성 단계에서만 이루어집니다.
|
|
15
|
+
|
|
16
|
+
**이 설계는 결정적 익명성을 주장하지 않습니다.** 목표는 "벤더 식별 불가"가 아니라 **"벤더 식별이 심판의 기본 관찰 경로에 놓이지 않음"** 수준입니다. 라벨 뒤에 벤더가 계속 숨겨진다고 가정하지 마십시오 — 남는 누출 경로는 [신뢰 경계](#신뢰-경계)에 열거되어 있습니다.
|
|
17
|
+
|
|
18
|
+
리뷰어 3벤더(`claude -p`, `omx exec`, `agy -p`)와 심판 로테이션 3슬롯은 **모델 단위로만** 겹치지 않습니다(스펙 REQ-3). 겹치는 축과 겹치지 않는 축을 구분해 적습니다.
|
|
19
|
+
|
|
20
|
+
**아래 표는 리뷰어를 벤더 슬러그(`claude`/`omx`/`agy`)로 지칭합니다 — `A`/`B`/`C`가 아닙니다.** A/B/C는 라운드마다 시드 `agora-<epoch>-r<N>`으로 다시 섞이는 익명 라벨이므로 어떤 벤더에도 고정되지 않으며, **"리뷰어 A" 같은 고정 벤더는 존재하지 않습니다**. 따라서 게이트가 출력하는 `리뷰어: A BUILD_WITH_CHANGES · B BUILD · C REDESIGN`을 벤더 판정으로 읽지 마십시오 — 같은 세션 안에서도 다음 라운드의 `A`는 다른 벤더입니다. 라벨과 벤더의 대응은 `report.md`에서만 공개됩니다.
|
|
21
|
+
|
|
22
|
+
| 축 | 겹침 |
|
|
23
|
+
|----|------|
|
|
24
|
+
| 모델 ID | 없음 — 리뷰어는 `claude-opus-4-8`(`claude` CLI) / `omx` 기본 모델(`omx` CLI) / `gemini-3.1-pro-high`(`agy` CLI), 심판 슬롯은 1: `claude-opus-5`(`claude` CLI) / 2: `claude-opus-4-6-thinking`(`agy` CLI) / 3: `gpt-oss-120b-medium`(`agy` CLI) |
|
|
25
|
+
| CLI 바이너리 | **있음** — `claude` 바이너리는 `claude` 리뷰어와 심판 슬롯 1이, `agy` 바이너리는 `agy` 리뷰어와 심판 슬롯 2·3이 공유합니다 |
|
|
26
|
+
| 모델 계열 | **있음** — 심판 3슬롯 중 2슬롯(`claude-opus-5`, `claude-opus-4-6-thinking`)이 `claude` 리뷰어의 모델(`claude-opus-4-8`)과 같은 Claude 계열입니다. 이 2슬롯은 CLI로는 각각 `claude`와 `agy`이므로 CLI 축과 계열 축은 서로 다른 모양으로 겹칩니다 |
|
|
27
|
+
|
|
28
|
+
따라서 "리뷰-판정 간 교차 오염이 없다"고 말할 수 있는 범위는 **모델 ID 단위까지**입니다. 같은 계열 모델이 공유하는 사전 학습 편향은 이 분리로 제거되지 않습니다.
|
|
29
|
+
|
|
30
|
+
## 라운드 파이프라인
|
|
31
|
+
|
|
32
|
+
각 라운드는 세 스크립트를 순서대로 호출합니다.
|
|
33
|
+
|
|
34
|
+
| 단계 | 명령 | 산출 |
|
|
35
|
+
|------|------|------|
|
|
36
|
+
| 리뷰어 | `bash scripts/reviewers.sh --run --session-dir <dir> --round <N> --prompt-file <f>` | `SEALED/raw/round-N/{claude,omx,agy}.json` |
|
|
37
|
+
| 익명화 | `bash scripts/anonymize.sh --build --session-dir <dir> --round <N> --seed agora-<epoch>-r<N> --topic <t> --attachments <json> --agenda <json>` | `SEALED/mapping/round-N.json` + `anon/round-N.json` |
|
|
38
|
+
| 심판 | `bash scripts/judge.sh --run --anon-file <dir>/anon/round-N.json --out-file <dir>/verdict/round-N.json --round <N>` | `verdict/round-N.json` |
|
|
39
|
+
| 종료 판정 | `bash scripts/agora.sh --decide-stop < <dir>/state.json` | `CONSENSUS\|STALLED\|MAX_ROUNDS\|USER\|CONTINUE` |
|
|
40
|
+
|
|
41
|
+
라운드 1은 백지 상태로 진행됩니다 — `agenda`, `prior_rounds`, 심판 초안 없이 `topic`과 `attachments`만 리뷰어에게 전달됩니다. 세션 전체에서 진짜 독립 의견을 얻는 유일한 라운드이기 때문입니다. 라운드 2부터는 직전 심판의 `agenda` + 직전 2라운드의 `prior_rounds` + 직전 초안이 함께 전달되어 수렴을 유도합니다.
|
|
42
|
+
|
|
43
|
+
### CLI 진입점
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
bash agora.sh --decide-stop < <dir>/state.json
|
|
47
|
+
bash agora.sh --start "<topic>" [--attach <path>]... [--max-rounds <N>] [--auto]
|
|
48
|
+
bash agora.sh --round <N> --session-dir <dir> [--extra-agenda <json-array>]
|
|
49
|
+
bash agora.sh --gate --session-dir <dir> --round <N>
|
|
50
|
+
bash agora.sh --set-stop <CODE> --session-dir <dir>
|
|
51
|
+
bash agora.sh --report --session-dir <dir>
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
| 진입점 | 파일을 쓰는가 | 실행 주체 |
|
|
55
|
+
|--------|:---:|-----------|
|
|
56
|
+
| `--decide-stop` | 아니오 | `agora-runner` (라운드 실행 직후 정지 코드 산출) |
|
|
57
|
+
| `--start` | **예** | `agora-runner` |
|
|
58
|
+
| `--round` | **예** | `agora-runner` |
|
|
59
|
+
| `--gate` | 아니오 | 오케스트레이터 (사용자 상호작용) |
|
|
60
|
+
| `--set-stop` | **예** | `agora-runner` |
|
|
61
|
+
| `--report` | **예** | `agora-runner` |
|
|
62
|
+
|
|
63
|
+
#### 채널 계약
|
|
64
|
+
|
|
65
|
+
`--start`의 **stdout에는 세션 디렉토리 절대경로 한 줄만** 실립니다. 게이트드 모드에서 라운드 1의 게이트 블록은 **stderr로 나갑니다** — 문서화된 관용구 `dir=$(bash agora.sh --start ...)`가 stdout을 통째로 캡처하므로, 게이트가 stdout에 있으면 `$dir`이 게이트 17줄에 경로가 붙은 문자열이 되어 쓸 수 없게 되기 때문입니다.
|
|
66
|
+
|
|
67
|
+
반면 **독립 서브커맨드 `--gate`는 블록을 stdout에 출력합니다** — 그 서브커맨드에서는 게이트 렌더링이 부산물이 아니라 산출물 전부이기 때문입니다.
|
|
68
|
+
|
|
69
|
+
`agora-runner`가 `--start`를 실행하면 게이트 블록은 러너의 stderr로 흘러가 반환 계약에 담기지 못합니다. 오케스트레이터는 `--start` 반환 후 `--gate --session-dir <dir> --round 1`로 게이트를 **다시 렌더**해 사용자에게 표시하십시오.
|
|
70
|
+
|
|
71
|
+
#### `--set-stop <CODE>`
|
|
72
|
+
|
|
73
|
+
`.stop` 필드(= `report.md`의 `종료 사유`)를 기록하는 **유일한 수단**입니다. 게이트드 모드에서 라운드 2 이후에는 `.stop`에 다른 writer가 없으므로, 이 명령을 건너뛰면 세션이 아무리 깨끗하게 끝나도 `report.md`가 `종료 사유: UNKNOWN`을 출력합니다.
|
|
74
|
+
|
|
75
|
+
| 항목 | 값 |
|
|
76
|
+
|------|-----|
|
|
77
|
+
| 허용 코드 | `CONSENSUS` · `STALLED` · `MAX_ROUNDS` · `USER` |
|
|
78
|
+
| 거부 코드 | `CONTINUE` → exit 64. "아직 정지하지 않았다"는 뜻이므로 종료 사유로 기록될 수 없습니다 — 다음 라운드를 도십시오 |
|
|
79
|
+
| 그 밖의 미지 코드 | exit 64. stderr에 허용 목록을 출력합니다 |
|
|
80
|
+
| 코드 자체가 없거나 `--`로 시작 | exit 64 |
|
|
81
|
+
| 범위 | 세션 단위(`--round` 없음). `.stop`은 세션이 **어떻게** 끝났는지를, `.round`가 **어디서** 끝났는지를 담습니다. 재기록은 멱등이라 무해합니다 |
|
|
82
|
+
|
|
83
|
+
허용 코드 집합은 하드코딩이 아니라 `decide_stop` 함수 본문에서 런타임에 읽어냅니다. 정지 조건이 추가·개명되면 자동으로 따라갑니다.
|
|
84
|
+
|
|
85
|
+
**절차**: 호출자는 매 라운드 `--decide-stop` 결과가 `CONTINUE`가 **아니면** 그 코드를 그대로 `--set-stop`으로 기록한 뒤 `--report`를 실행합니다. 사용자가 게이트에서 `s`를 고른 경우도 `--set-stop USER`로 직접 기록해야 합니다. (`--start --auto`와 게이트드 `--start`의 라운드 1은 이 기록을 스크립트가 내부에서 수행하므로 예외입니다.)
|
|
86
|
+
|
|
87
|
+
`--extra-agenda`는 게이트의 `e` 옵션을 실행하는 유일한 수단입니다. 값은 **JSON 배열 문자열**이어야 하며(예: `--extra-agenda '["롤백 명령 시퀀스를 제시할 것"]'`), 심판 `agenda[]` 뒤에 이어붙습니다. 문자열 하나를 그대로 넘기면 `jq` 병합이 실패합니다. 생략 시 기본값은 `[]`입니다.
|
|
88
|
+
|
|
89
|
+
환경 오버라이드:
|
|
90
|
+
|
|
91
|
+
| 변수 | 기본값 | 용도 |
|
|
92
|
+
|------|--------|------|
|
|
93
|
+
| `AGORA_SESSION_EPOCH` | 없음 (자동 생성) | 세션 시드 고정 (재현용) |
|
|
94
|
+
| `AGORA_OUTPUT_ROOT` | `.claude/outputs/sessions` | 아티팩트 루트 |
|
|
95
|
+
| `AGORA_TIMEOUT_SECS` | `300` | 리뷰어/심판 CLI 타임아웃 |
|
|
96
|
+
| `AGORA_CLAUDE_BIN` | `claude` (PATH 탐색) | 리뷰어·심판의 Claude CLI 경로 오버라이드 |
|
|
97
|
+
| `AGORA_AGY_BIN` | `agy` (PATH 탐색) | 리뷰어·심판의 agy CLI 경로 오버라이드 |
|
|
98
|
+
| `AGORA_OMX_BIN` | **`/opt/homebrew/bin/omx` (절대 경로)** | 리뷰어 omx CLI 경로 오버라이드. 다른 셋과 달리 PATH를 탐색하지 않으므로, omx가 이 경로에 없는 머신에서는 반드시 설정해야 합니다 |
|
|
99
|
+
| `AGORA_FORCE_TIMEOUT_FALLBACK` | `0` | **테스트 전용** — `gtimeout` 존재 여부와 무관하게 wait 기반 타임아웃 폴백 경로를 강제합니다. 운영 실행에서는 설정하지 마십시오 |
|
|
100
|
+
| `AGORA_VERDICT_SCHEMA` | 스크립트 옆의 `verdict-schema.json` | **테스트 전용** — `judge.sh`가 검증에 쓰는 스키마 경로 대체. 존재하지 않는 경로를 가리켜 exit 68 경로를 재현하는 용도 |
|
|
101
|
+
|
|
102
|
+
이 변수들은 **자식 CLI에는 전달되지 않습니다** — 아래 [벤더 호출 환경 정화](#벤더-호출-환경-정화)를 참조하십시오.
|
|
103
|
+
|
|
104
|
+
## 권한 우회 플래그 (실행 전 반드시 확인)
|
|
105
|
+
|
|
106
|
+
**이 스킬은 리뷰어 벤더 CLI 중 둘을 권한 프롬프트를 건너뛰는 플래그와 함께 실행합니다.** 운영자 승인을 요구하지 않고 자동으로 붙으므로, 스킬을 돌리기 전에 이 사실을 알고 있어야 합니다.
|
|
107
|
+
|
|
108
|
+
| 호출 | CLI | 붙는 플래그 |
|
|
109
|
+
|------|-----|-------------|
|
|
110
|
+
| 리뷰어 | `claude` | `--enable-auto-mode` |
|
|
111
|
+
| 리뷰어 | `omx` | 없음 |
|
|
112
|
+
| 리뷰어 | `agy` | `--dangerously-skip-permissions` |
|
|
113
|
+
| 심판 슬롯 1 | `claude` | **없음** |
|
|
114
|
+
| 심판 슬롯 2·3 | `agy` | **없음** |
|
|
115
|
+
|
|
116
|
+
**리뷰어에 붙는 이유**: 이 두 이름은 사용자 셸의 **별칭(alias)**이며 별칭 정의에 해당 플래그가 이미 들어 있습니다. 별칭은 비대화형 `bash script.sh` 실행에서 확장되지 않으므로, 스크립트가 명시적으로 붙이지 않으면 CLI가 대화형 실행과 다르게 동작합니다 — `claude`는 auto mode 없이 돌고, `agy`는 권한 프롬프트에서 멈춰 라운드 타임아웃(→ 결측)에 걸립니다. `omx`는 별칭이 아닌 실제 바이너리라 추가 플래그가 없습니다.
|
|
117
|
+
|
|
118
|
+
**심판에는 붙지 않습니다 — 비대칭은 실재합니다.** `judge.sh`는 같은 두 CLI를 호출하면서 두 플래그를 모두 생략하며, 심판 CLI에 넘어가는 인자는 `-p --model` (그리고 `agy`의 경우 `--output-format json --json-schema`)와 프롬프트 문자열뿐입니다. 스크립트 주석은 리뷰어 쪽 플래그의 근거만 적고 심판 쪽 생략의 근거는 적지 않으므로, **여기서는 관측된 사실만 기술합니다**: 심판은 리뷰어보다 낮은 도구 권한으로 실행되며, 심판이 권한 프롬프트를 띄우는 환경에서는 그 슬롯이 타임아웃되어 로테이션이 다음 슬롯으로 전진합니다(3슬롯 모두 그러면 exit `4`). 이 비대칭은 심판이 익명 번들 외의 자료에 손대지 못하게 하는 방향과는 정합하지만, 코드가 그 의도를 명시하지는 않았습니다.
|
|
119
|
+
|
|
120
|
+
## 종료 코드
|
|
121
|
+
|
|
122
|
+
스크립트 4종이 실제로 반환하는 코드 전부입니다. `agora.sh`는 `reviewers.sh`/`anonymize.sh`/`judge.sh`의 코드를 **그대로 전파**하므로(예외: 68은 자체 진단 한 줄을 덧붙인 뒤 전파), 오케스트레이터가 `--round`에서 보는 코드는 아래 전부가 될 수 있습니다.
|
|
123
|
+
|
|
124
|
+
| 코드 | 반환 스크립트 | 의미 | 재시도 |
|
|
125
|
+
|------|--------------|------|:---:|
|
|
126
|
+
| `0` | 전부 | 성공 | — |
|
|
127
|
+
| `1` | `anonymize.sh` | **지문 검출** — 검사 대상 텍스트에서 금칙 패턴이 걸림. 익명성 파기를 막기 위한 하드 스톱 | 아니오 |
|
|
128
|
+
| `3` | `reviewers.sh` · `anonymize.sh` | **유효 리뷰어 2인 미만**으로 라운드 중단. 두 스크립트가 같은 코드를 쓰는 것은 의도적이며, 어느 단계에서 걸렸는지는 stderr로 구분합니다(아래 [결측 판정](#결측-판정은-2단계입니다)) | 아니오 |
|
|
129
|
+
| `4` | `judge.sh` | 심판 로테이션 3슬롯이 **전부** 실패 — CLI 실패·타임아웃·JSON 파싱 실패·스키마 위반을 모두 포함 | 아니오 (로테이션 대체까지가 이미 시도의 전부) |
|
|
130
|
+
| `64` | 전부 | 사용법 오류 — 미지의 옵션, 필수 플래그 누락, `--extra-agenda`가 JSON 배열이 아님, `--set-stop`의 코드 누락·미지 코드·`CONTINUE` | 아니오 |
|
|
131
|
+
| `65` | `anonymize.sh` | 직전 라운드의 sealed 데이터가 **존재하는데 파싱 불가**(무결성 문제) 또는 미지의 벤더 식별자. `1`(지문)과 일부러 구분합니다 — "번들이 벤더를 누출할 뻔했다"와 "직전 라운드 데이터가 손상됐다"는 운영자 대응이 다릅니다 | 아니오 |
|
|
132
|
+
| `66` | `agora.sh` · `judge.sh` | 입력 부재 — 세션 디렉토리를 찾을 수 없음(`agora.sh`) / `--anon-file`이 없음(`judge.sh`) | 아니오 |
|
|
133
|
+
| `68` | `judge.sh` | **설정 오류** — `verdict-schema.json`을 읽거나 파싱할 수 없음. 심판의 실패가 아니라 배포 설정의 결함이므로 재시도해도 같은 원인으로 즉시 재실패합니다 | 아니오 |
|
|
134
|
+
| `73` | `agora.sh` | **기록 실패** — `state.json` 또는 `report.md`를 쓸 수 없음. 아래 별항 참조 | 아니오 |
|
|
135
|
+
|
|
136
|
+
내부 전용 코드: `124`(타임아웃)는 `run_with_timeout`이 GNU `timeout` 관례에 맞춰 정규화한 값이며 **호출자에게 그대로 노출되지 않습니다** — 리뷰어에서는 결측으로(→ `3`), 심판에서는 로테이션 전진으로(→ `4`) 흡수됩니다. `reviewers.sh`/`judge.sh`의 `65`(미지 CLI 슬러그)도 로스터가 하드코딩이라 실제로는 도달하지 않습니다.
|
|
137
|
+
|
|
138
|
+
### exit 73 — 라운드는 돌았는데 기록되지 않음
|
|
139
|
+
|
|
140
|
+
`73`(EX_CANTCREAT)은 다른 코드와 성격이 완전히 다릅니다. 여기까지 왔다는 것은 **리뷰어 3벤더와 심판이 이미 호출되어 과금까지 끝났다**는 뜻이고, 실패한 것은 그 결과를 `state.json`(또는 `report.md`)에 남기는 마지막 단계뿐입니다.
|
|
141
|
+
|
|
142
|
+
오케스트레이터의 대응:
|
|
143
|
+
|
|
144
|
+
- **산출물을 소비하지 마십시오.** `state.json`이 갱신되지 않았으므로 `.round`가 뒤처져 있고, 그 상태에서 `--decide-stop`은 `MAX_ROUNDS`/`STALLED`를 영원히 내지 못하며 `--report`는 뒤처진 라운드의 verdict를 읽습니다.
|
|
145
|
+
- **라운드를 재실행하지 마십시오.** 벤더가 다시 과금됩니다.
|
|
146
|
+
- 실패 원인(디렉토리 권한, 디스크, 심판이 내놓은 비정수 `new_findings` 등)을 stderr 진단 줄에서 확인해 사용자에게 보고하고 지시를 기다리십시오.
|
|
147
|
+
|
|
148
|
+
`--set-stop`이 73을 반환한 경우도 같습니다 — 종료 사유가 기록되지 않았으므로 `--report`를 실행하면 `종료 사유: UNKNOWN`이 박힌 보고서가 나옵니다. 보고서를 만들기 전에 멈추십시오.
|
|
149
|
+
|
|
150
|
+
### 결측 판정은 2단계입니다
|
|
151
|
+
|
|
152
|
+
리뷰어 결측은 **서로 다른 두 스크립트가 순차로** 판정하며, 둘 다 하한은 "유효 응답 2인"이고 둘 다 exit `3`입니다.
|
|
153
|
+
|
|
154
|
+
| 단계 | 스크립트 | 무엇을 결측으로 세는가 |
|
|
155
|
+
|------|----------|----------------------|
|
|
156
|
+
| 1 | `reviewers.sh` | CLI가 응답하지 못한 벤더 — 타임아웃(124), 비영 종료, **구문상 JSON이 아닌 출력**. 각 벤더는 2회 시도(최초 + 재시도 1회) 후 결측 처리되며, 결측 벤더는 파일을 아예 남기지 않습니다. 2개 이상 결측이면 exit 3 |
|
|
157
|
+
| 2 | `anonymize.sh` | 파일이 없거나, 있어도 **응답 스키마 계약을 위반**한 벤더(`overall` enum, `findings[]`의 `severity`/`verdict` enum, 빈 문자열 금지 필드 등). 이 검사는 `reviewers.sh`가 통과시킨 뒤에 걸리므로 별도 하한이 필요합니다 — 유효 응답이 2 미만이면 exit 3 |
|
|
158
|
+
|
|
159
|
+
2단계 하한이 없으면 "1개 유효 + 2개 스키마 위반"이 리뷰어 1인짜리 번들을 만들고, 심판이 단일 의견을 놓고 합의를 판정하게 됩니다. 두 단계의 코드가 같으므로 운영자에게 보이는 의미("리뷰어 부족으로 라운드 중단")는 어느 쪽이 걸렸든 동일하며, 구분이 필요하면 stderr 메시지를 보십시오 — 1단계는 `2 or more reviewers missing`, 2단계는 `valid reviewer response(s); 2 or more are required`입니다.
|
|
160
|
+
|
|
161
|
+
### 심판 응답 검증
|
|
162
|
+
|
|
163
|
+
`judge.sh`는 심판 출력이 구문상 JSON인지만 보지 않고, `verdict-schema.json`을 기준으로 세 가지를 검사합니다. 필수 필드 목록·선언 타입·enum 값을 모두 **스키마 파일에서 읽어오므로** 스키마가 바뀌면 검사도 따라갑니다.
|
|
164
|
+
|
|
165
|
+
| 검사 | 위반 예 |
|
|
166
|
+
|------|---------|
|
|
167
|
+
| 필수 필드 존재(그리고 non-null) | `draft` 누락 |
|
|
168
|
+
| **선언 타입 일치** | 스키마가 배열로 선언한 `agenda`에 `"1. 단일 의제"` 문자열이 옴 |
|
|
169
|
+
| enum 값(`consensus`·`verdict`) | `verdict: "MERGE"` |
|
|
170
|
+
|
|
171
|
+
셋 중 하나라도 걸리면 그 슬롯은 실패로 처리되어 **로테이션이 다음 슬롯으로 전진**하고, 3슬롯이 모두 소진되면 exit `4`입니다. 타입 검사가 특히 중요한 이유는, 타입이 틀린 `agenda`가 검증을 통과해 저장되면 다음 라운드의 `jq '. + $extra'` 병합이 죽고 → 의제가 빈 값으로 붕괴하고 → 리뷰어 프롬프트에 의제 섹션이 **비어 있는 채로** 3벤더가 전부 호출·과금된 뒤에야 문제가 드러나기 때문입니다.
|
|
172
|
+
|
|
173
|
+
## 사용자 게이트
|
|
174
|
+
|
|
175
|
+
`mode: gated`(기본값)에서 매 라운드 종료 후 오케스트레이터가 아래 형식을 표시합니다. 벤더는 노출하지 않고 라벨만 노출합니다 — 벤더 공개는 `report.md` 생성 단계에서만 이루어집니다.
|
|
176
|
+
|
|
177
|
+
```
|
|
178
|
+
─── Agora Round 2/5 ───────────────────────────────
|
|
179
|
+
심판: (모델 로테이션 #2)
|
|
180
|
+
Consensus: MAJORITY Verdict: BUILD_WITH_CHANGES
|
|
181
|
+
리뷰어: A BUILD_WITH_CHANGES · B BUILD · C REDESIGN
|
|
182
|
+
신규 지적: 2건 최고 심각도: HIGH
|
|
183
|
+
라운드 소요: 412초 (참고: 심판 로테이션 3슬롯 순차 재시도 시 최악 약 15분)
|
|
184
|
+
|
|
185
|
+
해소됨(1)
|
|
186
|
+
F1 상태 파일 경합 — 세션 디렉토리 격리로 충분
|
|
187
|
+
|
|
188
|
+
미해소(1)
|
|
189
|
+
F4 [HIGH] 롤백 경로 부재 — A REJECT / C KEEP / B 미언급
|
|
190
|
+
|
|
191
|
+
다음 라운드 의제
|
|
192
|
+
- F4 의 심각도 판정 근거를 각자 제시할 것
|
|
193
|
+
- 롤백 경로의 구체적 명령 시퀀스
|
|
194
|
+
|
|
195
|
+
[c] 계속 [s] 중단하고 보고서 [e] 의제 추가 후 계속
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
**`리뷰어:` 줄의 A/B/C는 벤더가 아닙니다.** 이 라벨은 해당 라운드의 시드로 새로 섞인 것이라 라운드마다 가리키는 벤더가 바뀝니다. `A BUILD_WITH_CHANGES`를 특정 벤더의 입장으로 읽지 마십시오 — 라운드 간 비교도 불가합니다. 벤더 대응은 `report.md`의 「라운드별 참여자(익명 해제)」에서만 확인할 수 있습니다. 결측 벤더가 있으면 이 줄에는 라벨이 2개만 나옵니다.
|
|
199
|
+
|
|
200
|
+
**토큰 누적은 표시되지 않습니다.** 벤더 CLI들이 토큰 사용량을 일관된 형식으로 보고하지 않아 계측을 신뢰할 수 없으므로 구현하지 않았습니다 — `state.json`의 `history[].tokens`는 항상 `0`으로 기록됩니다. 게이트에 누적 토큰 줄을 두는 설계 스펙 §10과의 **의도적 차이**입니다. 비용 감각은 아래 [`--auto` 경고](#--auto-경고)의 라운드당 추정치로 대체하고, 게이트가 제공하는 실측값은 `라운드 소요` 초 하나뿐임을 전제하십시오.
|
|
201
|
+
|
|
202
|
+
| 키 | 동작 |
|
|
203
|
+
|----|------|
|
|
204
|
+
| `c` | 다음 라운드 진행. 심판 의제를 그대로 사용 |
|
|
205
|
+
| `s` | 루프 종료(`stop: "USER"`), 현재까지의 결과로 `report.md` 생성 |
|
|
206
|
+
| `e` | 사용자 의제를 입력받아 심판 `agenda[]`에 **추가**한 뒤 다음 라운드 진행 |
|
|
207
|
+
|
|
208
|
+
`e`는 추가만 가능하며 심판 의제를 덮어쓰지 못합니다. 사용자가 심판 의제를 삭제할 수 있으면 심판의 의제 설정 권한이 형해화되고, 사용자가 불편해하는 쟁점이 조용히 사라지는 경로가 생기기 때문입니다.
|
|
209
|
+
|
|
210
|
+
### 게이트드 모드의 책임 분리
|
|
211
|
+
|
|
212
|
+
게이트드 모드에서 **루프를 도는 주체는 스크립트가 아니라 호출자**입니다. 스크립트는 라운드 하나만 실행하고 반환하며, 정지 여부를 스스로 판단하지 않습니다.
|
|
213
|
+
|
|
214
|
+
| 명령 | 하는 일 | 하지 않는 일 |
|
|
215
|
+
|------|---------|--------------|
|
|
216
|
+
| `--start` (`--auto` 없이) | 세션 생성 + **라운드 1만** 실행 후 반환 | 라운드 2 이후를 돌지 않음 |
|
|
217
|
+
| `--round <N>` | 지정 라운드를 실행하고 `state.json`에 기록 | `--decide-stop`을 호출하지 않음. `.stop`도 `max_rounds`도 **검사하지 않음** |
|
|
218
|
+
| `--gate` | 게이트 화면 렌더 (stdout) | 사용자 입력을 받지 않음 |
|
|
219
|
+
| `--set-stop` | `.stop`에 종료 사유 기록 | 정지 여부를 스스로 판단하지 않음 — 호출자가 준 코드를 그대로 씀 |
|
|
220
|
+
| `--report` | `report.md` 생성 | 정지 판단을 하지 않음. `.stop`을 쓰지도 않음 — 미기록이면 `UNKNOWN`을 출력 |
|
|
221
|
+
|
|
222
|
+
- **라운드 상한은 `--round`를 막지 않습니다.** `max_rounds`를 넘긴 라운드도, 이미 `.stop`이 기록된 세션의 라운드도 거부 없이 실행됩니다. 정지 판단은 전적으로 호출자 몫입니다.
|
|
223
|
+
- **게이트드 `--start`가 게이트 없이 끝나는 경로가 있습니다.** 라운드 1에서 종료 조건이 걸리면 `.stop` 기록과 `report.md` 생성까지 수행한 뒤 게이트를 렌더하지 않고 반환합니다. 따라서 `--start` 반환 후에는 게이트 출력 유무가 아니라 `state.json`의 `.stop`을 읽어 분기하십시오.
|
|
224
|
+
|
|
225
|
+
여기서 **호출자는 두 층**입니다 — 오케스트레이터(메인 대화)와 `agora-runner` 서브에이전트. 파일을 쓰는 명령(`--start`·`--round`·`--set-stop`·`--report`)은 `agora-runner`가, 파일을 쓰지 않는 순수 출력 명령(`--gate`)은 오케스트레이터가 실행합니다(R010). 매 라운드 흐름은 다음과 같습니다.
|
|
226
|
+
|
|
227
|
+
1. **오케스트레이터 → `agora-runner` 위임**: 러너가 `--round <N> --session-dir <dir>` 를 실행한 뒤 `--decide-stop < <dir>/state.json` 으로 정지 코드를 산출해, **verdict 요약 + `stop_code`** 를 반환합니다.
|
|
228
|
+
2. **오케스트레이터가 직접 실행**: `--gate --session-dir <dir> --round <N>` 로 게이트를 표시하고 사용자 응답(`c`/`s`/`e`)을 받습니다. `--gate`는 파일을 쓰지 않으므로 오케스트레이터가 실행해도 R010에 걸리지 않으며, 서브에이전트는 사용자와 상호작용하지 않으므로 이 단계를 위임할 수도 없습니다.
|
|
229
|
+
3. **계속이면 다음 라운드를 다시 위임**하고(사용자가 `e`를 골랐으면 `--extra-agenda <json-array>` 동반), **정지면 `--set-stop <CODE>` + `--report --session-dir <dir>` 를 `agora-runner`에 위임**합니다. `<CODE>`는 1단계의 `stop_code`를 그대로 쓰되, 사용자가 `s`를 골라 멈추는 경우에는 `USER`입니다. 이 기록을 건너뛰면 보고서가 `종료 사유: UNKNOWN`으로 나옵니다.
|
|
230
|
+
|
|
231
|
+
## `--auto` 경고
|
|
232
|
+
|
|
233
|
+
**`--auto`는 매 라운드 게이트를 생략합니다.** 비용 상한이 명확할 때만 사용하십시오.
|
|
234
|
+
|
|
235
|
+
| 항목 | 값 |
|
|
236
|
+
|------|-----|
|
|
237
|
+
| 라운드당 토큰 추정 | 60~120k (리뷰어 3 + 심판 1) |
|
|
238
|
+
| 기본 라운드 상한 | 5 (`--max-rounds`로 조정) |
|
|
239
|
+
| **최악의 경우 총 토큰** | **약 600k** |
|
|
240
|
+
|
|
241
|
+
**소요 시간 주의**: 리뷰어 단계와 심판 단계는 병렬성이 다르므로 벽시계 계산이 다릅니다.
|
|
242
|
+
|
|
243
|
+
| 단계 | 실행 형태 | 최악 벽시계 (`AGORA_TIMEOUT_SECS`=300 기준) |
|
|
244
|
+
|------|-----------|-------------------------------------------|
|
|
245
|
+
| 리뷰어 | 3벤더 **병렬 팬아웃**(`&` + `wait`), 벤더당 최초 1회 + 재시도 1회 | 300초 × 2시도 = **600초**. 벤더 수는 벽시계에 곱해지지 않습니다 |
|
|
246
|
+
| 심판 | 로테이션 3슬롯 **순차** 시도 | 300초 × 3슬롯 = **900초** |
|
|
247
|
+
| 라운드 합계 | 두 단계는 순차 | **약 1500초 ≈ 25분** |
|
|
248
|
+
|
|
249
|
+
기본 상한 5라운드를 `--auto`로 끝까지 돌면 최악 약 2시간이며, 게이트가 없으므로 이 지연이 중단 없이 누적됩니다. (게이트가 표시하는 `참고: 심판 로테이션 3슬롯 순차 재시도 시 최악 약 15분`은 **심판 단계만**의 값이며 라운드 전체가 아닙니다.)
|
|
250
|
+
|
|
251
|
+
## 신뢰 경계
|
|
252
|
+
|
|
253
|
+
익명성은 프롬프트 지시가 아니라 **디렉토리 경계**로 유지됩니다.
|
|
254
|
+
|
|
255
|
+
| 주체 | `SEALED/` 접근 | `anon/` 접근 |
|
|
256
|
+
|------|:---:|:---:|
|
|
257
|
+
| 오케스트레이터 (메인 대화) | 금지 | 허용 |
|
|
258
|
+
| `agora-runner` 에이전트 | 금지 | 허용 |
|
|
259
|
+
| 리뷰어 CLI 3종 | 금지 | 금지 (라운드 내에서 서로를 보지 않음) |
|
|
260
|
+
| 심판 CLI | 금지 | 허용 (익명 번들만이 유일한 입력) |
|
|
261
|
+
| `anonymize.sh` | **허용** — `SEALED/raw/`(원문 읽기)와 `SEALED/mapping/`(직전 라운드 매핑 읽기 + 이번 라운드 매핑 봉인) | 허용 (`anon/round-N.json`을 생산하는 주체) |
|
|
262
|
+
| `report.md` 생성 단계 | **허용** — `SEALED/mapping/` 읽기 | 허용 (익명 해제가 이 단계의 목적) |
|
|
263
|
+
|
|
264
|
+
`SEALED/`를 읽는 주체는 **둘**입니다 — 파이프라인 안쪽의 `anonymize.sh`와 마지막의 보고서 생성 단계(스펙 §4). `anonymize.sh`는 봉인된 원문·매핑을 읽지만 그 산출물인 `anon/` 번들에는 라벨만 남기므로, 봉인을 읽는 것과 봉인을 푸는 것은 구분됩니다. 익명 해제는 `report.md` 생성 단계에서만 일어납니다.
|
|
265
|
+
|
|
266
|
+
**한계 1 — 격리를 지켜도 남는 누출**(스펙 §6): 위 표가 완벽히 지켜져도 심판은 벤더를 추론할 단서를 갖습니다. 익명성은 라벨이 유지되는 동안에도 아래 세 경로로 새어 나갑니다.
|
|
267
|
+
|
|
268
|
+
1. **라운드 내 일관성** — 한 라운드 안에서 하나의 라벨은 그 벤더의 전체 응답을 묶습니다. 심판은 "A의 findings 5건이 모두 성능 축에 집중되어 있다"와 같은 스타일 패턴을 관찰할 수 있습니다.
|
|
269
|
+
2. **라운드 간 일관성** — 재라벨링(과거 라운드의 라벨을 현재 라운드 매핑으로 변환)을 도입한 이상 `prior_rounds[]`를 통해 라운드 간 일관성도 관찰 가능해집니다. 이는 의도된 트레이드오프입니다 — "A가 입장을 바꿨다"는 오독을 막는 편익이 벤더 추론 위험보다 크다고 판단했습니다.
|
|
270
|
+
3. **논증 스타일 지문** — 서식 정규화는 문체 지문을 줄이지만 **논증 스타일 지문은 줄이지 못합니다**. 특정 모델이 반복적으로 특정 축을 파고드는 경향은 템플릿으로 흡수되지 않습니다.
|
|
271
|
+
|
|
272
|
+
그러므로 개요에 적은 대로 이 설계는 **결정적 익명성을 주장하지 않습니다**. 달성하는 것은 "벤더 식별 불가"가 아니라 "벤더 식별이 심판의 기본 관찰 경로에 놓이지 않음"이며, 심판이 추론을 시도하면 부분적으로 성공할 수 있다고 전제해야 합니다.
|
|
273
|
+
|
|
274
|
+
**한계 2 — 격리 자체가 강제되지 않음**: 이 격리는 **관례이지 하드 블록이 아닙니다**. 오케스트레이터도 심판 CLI도 `SEALED/`를 읽는 파일 읽기 능력 자체는 갖고 있습니다 — 읽지 못하는 것이 아니라 읽지 않도록 설계된 것입니다. PreToolUse 훅으로 `SEALED/` 경로 Read를 하드 블록하는 방안은 검토했으나, 훅은 프로젝트 전역에 영향을 주므로 초기 도입에서는 제외했습니다(위반율 관측 시 R021 Hard Enforcement Candidates 승격 검토 대상). 실질 방어선은 아래 지문 검사이며, 이는 "누출이 발생했는가"를 사후 탐지할 뿐 "누출을 시도할 수 없게" 만들지는 않습니다.
|
|
275
|
+
|
|
276
|
+
**한계 3 — 자식 CLI가 cwd를 상속함**: 벤더·심판 CLI는 `AGORA_*` 환경변수를 제거한 채 실행되지만(아래 [벤더 호출 환경 정화](#벤더-호출-환경-정화)), **작업 디렉토리는 상속합니다**. 기본 출력 루트 `AGORA_OUTPUT_ROOT`가 상대경로(`.claude/outputs/sessions`)이므로, 상속받은 cwd에서 `.claude/outputs/sessions/**`를 훑으면 세션 트리 — `SEALED/` 포함 — 에 도달할 수 있습니다. 환경 정화는 "세션 좌표를 **건네주지** 않는다"를 보장할 뿐 "찾을 수 **없게** 한다"를 보장하지 않습니다. 절대경로 `AGORA_OUTPUT_ROOT`를 세션 트리 밖으로 지정하면 이 경로는 좁아지지만, cwd 상속 자체는 남습니다.
|
|
277
|
+
|
|
278
|
+
### 지문 검사
|
|
279
|
+
|
|
280
|
+
`anon/round-N.json`을 그대로 검사하지 않습니다. `anonymize.sh`가 번들에서 **벤더 유래 텍스트만 뽑아낸 파생 문서**를 만들어 그것을 검사합니다.
|
|
281
|
+
|
|
282
|
+
| 필드 | 검사 대상 | 이유 |
|
|
283
|
+
|------|:---:|------|
|
|
284
|
+
| `reviewers[]` (이번 라운드) | **예** | 리뷰어 저작 — 익명화 대상 당사자 |
|
|
285
|
+
| `prior_rounds[].reviewers[]` | **예** | 재라벨링되어 다음 라운드 심판에게 전달되는 리뷰어 저작 텍스트 |
|
|
286
|
+
| `prior_rounds[].draft` · `.verdict` | **예** (운영자 어휘 제거 후) | 심판 저작. 심판은 익명화의 *주체*이지만, 그 초안·판정은 `relabel_prior`를 타고 **다음 라운드의 새 심판**에게 넘어갑니다. 심판이 쓴 귀속 추정("A는 Claude 계열로 보인다")이 후임에게 무검사로 전달되는 것이 바로 이 검사가 막으려는 누출입니다 |
|
|
287
|
+
| `topic` · `agenda` · `attachments` | **아니오** | **운영자 저작 — 의도적 제외(Ruling 10)**. 토론 주제 자체가 "제미나이를 도입할까"일 수 있으며, 주제가 벤더를 언급했다는 이유로 매 라운드가 중단되면 스킬이 성립하지 않습니다 |
|
|
288
|
+
| `round` 등 나머지 메타데이터 | 아니오 | 벤더 유래 텍스트가 아님 |
|
|
289
|
+
|
|
290
|
+
심판 저작 필드는 검사 전에 **운영자 어휘를 단어 단위로 걸러냅니다** — 심판이 주제·의제를 정당하게 인용할 수 있기 때문입니다. 필터는 토큰화 → 운영자 단어 제거 → 재결합 방식이며, **부분 문자열이 아니라 온전한 단어에만** 작동합니다(운영자 단어 "mini"가 "gemini"를 쪼개 검사를 무력화하는 역방향 회피를 막기 위함). `/`, `-`, `.`, `_`는 단어 문자로 취급되어 경로 형태와 버전 표기가 보존됩니다. 잔여 오탐(의도적, 차단 쪽으로 기움): 심판 텍스트의 운영자 단어에 한국어 조사가 붙으면(주제의 `제미나이` vs 심판의 `제미나이는`) 다른 토큰이라 면제되지 않고 중단시킵니다. 리뷰어 텍스트는 **필터하지 않습니다** — 리뷰어는 익명화 대상이므로 주제를 그대로 되뇌는 것도 누출로 취급합니다.
|
|
291
|
+
|
|
292
|
+
### 금칙 패턴이 실제로 막는 것
|
|
293
|
+
|
|
294
|
+
패턴은 대소문자 무시(`grep -Eiq`)로 적용되며 3계층입니다. **"모델명을 막는다"고 뭉뚱그리면 사실과 다릅니다** — 계층마다 걸리는 조건이 다릅니다.
|
|
295
|
+
|
|
296
|
+
| 계층 | 토큰 | 매칭 방식 |
|
|
297
|
+
|------|------|-----------|
|
|
298
|
+
| 1. 명백한 벤더·제품 토큰 | `codex` `omx` `gpt` `claude` `gemini` `antigravity` `anthropic` `openai` + 한글 음차(`클로드` `제미나이` `지피티` `앤트로픽` `오픈에이아이`) | **단어 경계 없는 부분 문자열**. 경계를 두면 `claudecode`나 `chatgpt` 같은 실제 자기 식별을 통과시키므로 일부러 뺐습니다 |
|
|
299
|
+
| 1'. `agy` | `agy` | **양쪽 단어 경계 유지**. 3글자라 무관한 단어(stagy, cagy, 성씨) 안에 들어갈 수 있고, 모델이 자기를 "agycode"라 부르는 형태도 없기 때문 |
|
|
300
|
+
| 2. 영어 일반 단어와 겹치는 모델 계열명 | `opus` `sonnet` `haiku` `flash` | **두 형태에서만** — (a) 버전 인접(`Opus 4.8`, `sonnet-5`, `flash 2.0`) (b) 비ASCII 문자 인접(`Sonnet 관점에서`). 맨 단어를 막으면 정상 리뷰 산문("magnum opus", "flash memory")에서 중단됩니다 |
|
|
301
|
+
| 3. 봉인 경로 형태 | `SEALED/` `/mapping/` `raw/round-` | 부분 문자열 |
|
|
302
|
+
|
|
303
|
+
실제로 이 세션에서 쓰이는 모델 ID는 전부 계층 1에 걸립니다 — `claude-opus-4-8`/`claude-opus-5`/`claude-opus-4-6-thinking`은 `claude`로, `gemini-3.1-pro-high`는 `gemini`로, `gpt-oss-120b-medium`은 `gpt`로, `omx:default`는 `omx`로 걸립니다.
|
|
304
|
+
|
|
305
|
+
**감수한 미탐(false negative)**: 버전도 한글 인접도 없이 영어 산문 안에 놓인 맨 모델 계열명 — 예: `Sonnet would argue` — 은 `sonnet-length prose` 와 정규식으로 구별할 수 없어 **통과합니다**. **감수한 오탐(false positive)**: `flash 메모리` 같은 한영 혼용 기술 용어는 계층 2-(b)에 걸려 라운드를 중단시킵니다. 오탐은 시끄럽고 복구 가능하지만 미탐은 조용히 익명성을 깨므로, 과차단 쪽을 택한 결과입니다.
|
|
306
|
+
|
|
307
|
+
지문이 걸리면 `anonymize.sh`가 exit `1`로 중단하며, `SEALED/mapping/`과 `anon/`에는 **아무것도 쓰이지 않습니다** — 매핑과 번들은 검사를 통과할 때까지 임시 디렉토리에 머뭅니다.
|
|
308
|
+
|
|
309
|
+
### 벤더 호출 환경 정화
|
|
310
|
+
|
|
311
|
+
`reviewers.sh`와 `judge.sh`는 자식 CLI를 실행할 때 **접두사 `AGORA_`로 시작하는 모든 환경변수를 제거**합니다(`env -u`, 실제 export된 이름을 `compgen -e`로 수집하므로 이름 목록 하드코딩이 아님). 스크립트가 그 값을 넘기지 않는다는 것만으로는 부족했기 때문입니다 — 운영자 셸에 `AGORA_OUTPUT_ROOT`가 export되어 있으면 그 값이 `agora.sh`를 거쳐 모든 벤더 CLI에 그대로 상속되고, 세션 트리(봉인 기록 포함)는 그 아래 한 단계입니다.
|
|
312
|
+
|
|
313
|
+
`AGORA_` 밖의 변수는 건드리지 않습니다 — `PATH`·`HOME`과 벤더 자신의 인증 토큰·프록시 설정은 그대로 살아 있어야 CLI가 인증할 수 있습니다. 읽는 시점과 넘기는 시점은 분리되어 있어, 스크립트 자신은 계속 `AGORA_TIMEOUT_SECS` 등을 평범한 셸 변수로 사용합니다(테스트 주입 설정이 여전히 동작하는 이유).
|
|
314
|
+
|
|
315
|
+
**이 정화가 덮지 못하는 것은 cwd입니다 — 위 [한계 3](#신뢰-경계)을 참조하십시오.**
|
|
316
|
+
|
|
317
|
+
## R010 위임 구조
|
|
318
|
+
|
|
319
|
+
오케스트레이터는 파일을 직접 쓸 수 없습니다(R010). `agora`는 라운드마다 다수의 아티팩트 파일을 기록하므로, 라운드 실행은 `agora-runner` 에이전트에 위임합니다.
|
|
320
|
+
|
|
321
|
+
- **위임 단위는 라운드 1개 = 위임 1건**입니다. 다중 라운드를 한 위임에 묶지 않습니다 — Phase 경계가 곧 mid-step 종료 지점이 되는 것을 방지하기 위함입니다(R020).
|
|
322
|
+
- **파일을 쓰는 진입점은 `--start`·`--round`·`--set-stop`·`--report` 넷**이며 모두 러너가 실행합니다. `--set-stop`은 이름이 판정처럼 보이지만 `state.json`을 변경하므로 오케스트레이터가 직접 실행하지 않습니다.
|
|
323
|
+
- `agora-runner`는 스크립트 실행과 아티팩트 기록을 담당하고, **verdict 요약만** 오케스트레이터에 반환합니다. 리뷰어 원문, 벤더 출처, `SEALED/` 경로는 반환하지 않습니다.
|
|
324
|
+
- **사용자 게이트 표시와 게이트 응답 처리는 오케스트레이터 전담**입니다 — 서브에이전트는 사용자와 상호작용하지 않습니다.
|
|
325
|
+
- `agora-runner` 에이전트 정의 자체는 이 스킬의 범위 밖입니다. `.claude/agents/agora-runner.md`를 참조하십시오.
|