okstra 0.46.0 → 0.48.0

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.
@@ -0,0 +1,251 @@
1
+ # Acceptance devil's-advocate critic 구현 계획 (sub-project B2)
2
+
3
+ > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
4
+
5
+ **Goal:** B1 의 critic primitive 를 `final-verification` 으로 확장해 "받아들이면 안 되는 이유/놓친 acceptance blocker" 를 캐는 악마의 변호인 패스를 추가하고, 후보 blocker 를 confirm-or-downgrade(확인→Acceptance Blocker, 미확인→Residual Risk)로 검증해 verdict 에 반영한다.
6
+
7
+ **Architecture:** selection·`--critic`·`convergence.critic` 블록·dispatch primitive 는 B1 그대로 재사용; 적용 phase 집합(render `critic_phases` + wizard `S_CRITIC_PICK.applies`)에 `final-verification` 만 추가. critic *행동*은 convergence skill 에서 phase 로 분기 — 3 finding-phase=coverage(B1), final-verification=acceptance devil's-advocate(신규, confirm-or-downgrade, Acceptance Blockers/Residual Risk 출력). 새 enum/validator 없음(기존 verdict↔blocker validator 재사용).
8
+
9
+ **Tech Stack:** Python 3 (okstra_ctl: render/wizard, pytest), Markdown skill/prompt 문서. 빌드: `npm run build`.
10
+
11
+ **설계 근거:** [`docs/superpowers/specs/2026-06-05-acceptance-critic-design.md`](../specs/2026-06-05-acceptance-critic-design.md)
12
+
13
+ ---
14
+
15
+ ## 파일 구조
16
+
17
+ | 파일 | 책임 | 작업 |
18
+ |---|---|---|
19
+ | [`scripts/okstra_ctl/render.py`](../../../scripts/okstra_ctl/render.py) | `critic_phases` 에 final-verification 추가 | Modify (928) |
20
+ | [`scripts/okstra_ctl/wizard.py`](../../../scripts/okstra_ctl/wizard.py) | `S_CRITIC_PICK.applies`(1979) + summary(2313) phase tuple 에 final-verification | Modify |
21
+ | [`tests/test_render_critic_block.py`](../../../tests/test_render_critic_block.py) | final-verification 비적용→적용 플립 | Modify |
22
+ | [`tests/test_wizard_critic_pick.py`](../../../tests/test_wizard_critic_pick.py) | final-verification skipped→applies 플립 | Modify |
23
+ | [`skills/okstra-convergence/SKILL.md`](../../../skills/okstra-convergence/SKILL.md) | "Acceptance critic pass (final-verification)" 절 + Index | Modify |
24
+ | [`prompts/profiles/final-verification.md`](../../../prompts/profiles/final-verification.md) | 악마의 변호인 critic opt-in 선언 + verdict 영향 | Modify |
25
+ | [`agents/SKILL.md`](../../../agents/SKILL.md) | Phase 5.6 critic 가 final-verification 에도 적용 | Modify |
26
+ | [`prompts/wizard/prompts.ko.json`](../../../prompts/wizard/prompts.ko.json) | `critic_pick` label phase-중립 일반화 | Modify |
27
+ | [`CHANGES.md`](../../../CHANGES.md) | 사용자 영향 항목 | Modify |
28
+
29
+ 작업 순서: 적용 phase 확장(render+wizard+test) → convergence skill 모드 → 프로필+agents+label → CHANGES+검증.
30
+
31
+ ---
32
+
33
+ ### B2-Task1: 적용 phase 에 final-verification 추가 (render + wizard + 테스트 플립)
34
+
35
+ **Files:**
36
+ - Modify: `scripts/okstra_ctl/render.py:928`
37
+ - Modify: `scripts/okstra_ctl/wizard.py` (1979, 2313)
38
+ - Modify: `tests/test_render_critic_block.py`
39
+ - Modify: `tests/test_wizard_critic_pick.py`
40
+
41
+ - [ ] **Step 1: 테스트를 새 기대값으로 갱신 (red 유발)**
42
+
43
+ (1a) `tests/test_render_critic_block.py`: `test_critic_enabled_for_applicable_phases` 의 parametrize 리스트에 `"final-verification"` 을 추가:
44
+ ```python
45
+ @pytest.mark.parametrize("task_type", ["requirements-discovery", "error-analysis", "implementation-planning", "final-verification"])
46
+ def test_critic_enabled_for_applicable_phases(task_type):
47
+ ```
48
+ 그리고 `test_critic_disabled_for_non_applicable_phases` 의 리스트에서 `"final-verification"` 을 삭제(남는 것: `["implementation", "release-handoff"]`).
49
+
50
+ (1b) `tests/test_wizard_critic_pick.py`: `test_critic_step_applies_for_three_phases_when_unset` 의 parametrize 리스트에 `"final-verification"` 추가(원하면 함수명도 `_for_applicable_phases` 로 변경 — 선택). `test_critic_step_skipped_for_other_phases` 리스트에서 `"final-verification"` 삭제(남는 것: `["implementation", "release-handoff"]`).
51
+
52
+ - [ ] **Step 2: 실패 확인**
53
+
54
+ Run: `python3 -m pytest tests/test_render_critic_block.py tests/test_wizard_critic_pick.py -q`
55
+ Expected: FAIL — final-verification 이 아직 critic 적용 phase 가 아님.
56
+
57
+ - [ ] **Step 3: render + wizard 구현**
58
+
59
+ (3a) `scripts/okstra_ctl/render.py:928` 의 줄
60
+ ` critic_phases = {"requirements-discovery", "error-analysis", "implementation-planning"}`
61
+ 를:
62
+ ` critic_phases = {"requirements-discovery", "error-analysis", "implementation-planning", "final-verification"}`
63
+
64
+ (3b) `scripts/okstra_ctl/wizard.py` 의 `S_CRITIC_PICK` Step applies(현 1979) 의 phase tuple
65
+ `s.task_type in ("requirements-discovery", "error-analysis", "implementation-planning")`
66
+ 를:
67
+ `s.task_type in ("requirements-discovery", "error-analysis", "implementation-planning", "final-verification")`
68
+
69
+ (3c) 같은 파일 summary 조건(현 2313) 의 동일 tuple 도 같은 4-phase tuple 로 교체.
70
+
71
+ (주의: 이 phase tuple 이 wizard.py 에 두 곳 — applies, summary — 있다. 둘 다 갱신. grep `"requirements-discovery", "error-analysis", "implementation-planning"` 로 누락 확인.)
72
+
73
+ - [ ] **Step 4: 통과 확인**
74
+
75
+ Run: `python3 -m pytest tests/test_render_critic_block.py tests/test_wizard_critic_pick.py -q`
76
+ Expected: PASS.
77
+
78
+ - [ ] **Step 5: 커밋**
79
+
80
+ ```bash
81
+ git add scripts/okstra_ctl/render.py scripts/okstra_ctl/wizard.py tests/test_render_critic_block.py tests/test_wizard_critic_pick.py
82
+ git commit -m "feat(okstra_ctl): enable coverage critic selection for final-verification"
83
+ ```
84
+
85
+ ---
86
+
87
+ ### B2-Task2: convergence skill — Acceptance critic pass (final-verification) 모드
88
+
89
+ **Files:**
90
+ - Modify: `skills/okstra-convergence/SKILL.md`
91
+
92
+ - [ ] **Step 1: 새 "## Acceptance critic pass (final-verification)" 절 추가**
93
+
94
+ 기존 `## Coverage critic pass` 절이 끝나는 지점 직후(다음 `## Output` 헤딩 **앞**)에 새 절 삽입:
95
+
96
+ ```markdown
97
+ ## Acceptance critic pass (final-verification)
98
+
99
+ The `final-verification` phase reuses the SAME reused-worker dispatch as §"Coverage critic pass" (provider + `config.critic.modelExecutionValue` from the `convergence.critic` block; default off; same model-unresolved skip rule). Only the prompt, the verification semantics, and the output sink differ — final-verification's findings are defects/blockers, so the critic acts as an **acceptance devil's advocate** (find reasons NOT to accept), and its candidate blockers are NEVER dropped (that would suppress real defects).
100
+
101
+ ### Prompt
102
+ ```
103
+ You are the acceptance devil's advocate for <task-key>. The delivered work is about
104
+ to be judged for acceptance. Your ONLY job is to find reasons it should NOT be
105
+ accepted — surface candidate acceptance BLOCKERS the verifiers may have missed:
106
+ - requirements / acceptance points with no covering evidence,
107
+ - DB / IO / SQL changes lacking real-execution evidence,
108
+ - regressions or broken error paths,
109
+ - scope / contract violations.
110
+ For each, emit a candidate blocker with a one-line statement, evidence (file:line /
111
+ log / test output), and a severity (critical / major / minor). Do NOT restate an
112
+ existing Acceptance Blocker. If you find none, say so explicitly.
113
+ ```
114
+
115
+ ### Verification — confirm-or-downgrade (BLOCKING)
116
+ Each candidate blocker is verified by the Phase 4 analysers (excluding the critic). Do NOT use the adversarial finding classifier's "uncertain → reject" rule here.
117
+ - **Confirmed** (an analyser reproduces it or cites supporting evidence) → promote to a `## 4 Acceptance Blockers` row (keep severity + recommended follow-up phase).
118
+ - **Not confirmed** (cannot reproduce, or evidence is weak) → **downgrade to a Residual Risk row — never drop it.** Record the escalation trigger so the user can re-judge a high-severity-but-unconfirmed candidate.
119
+
120
+ ### Verdict impact
121
+ Promoted blockers enter `## 4 Acceptance Blockers`; since `accepted` requires zero blockers, the verdict moves to `conditional-accept` / `blocked` automatically. The existing verdict↔blocker consistency validator (`validators/validate-run.py` `_validate_final_verification_consistency`) enforces this unchanged — no new enum or validator.
122
+
123
+ ### State
124
+ Critic output lives at `runs/final-verification/worker-results/<provider>-critic-final-verification-<seq>.md`. The convergence state `config.critic` summary (see §"Coverage critic pass") records `mode: "acceptance-devils-advocate"`, `candidatesProposed`, `confirmedBlockers`, `downgradedToResidual` (optional v1.2 fields; readers treat absence as null).
125
+ ```
126
+
127
+ - [ ] **Step 2: Index 항목 추가**
128
+
129
+ `## Index` 목록에서 `- [Coverage critic pass](#coverage-critic-pass)` 다음 줄에 추가:
130
+ ```markdown
131
+ - [Acceptance critic pass (final-verification)](#acceptance-critic-pass-final-verification)
132
+ ```
133
+
134
+ - [ ] **Step 3: 빌드 + 검증**
135
+
136
+ Run: `npm run build && bash validators/validate-workflow.sh`
137
+ Expected: build 22/22 synced, validator PASS.
138
+
139
+ - [ ] **Step 4: 커밋**
140
+
141
+ ```bash
142
+ git add skills/okstra-convergence/SKILL.md runtime/
143
+ git commit -m "feat(skills/okstra-convergence): acceptance devil's-advocate critic mode for final-verification"
144
+ ```
145
+
146
+ ---
147
+
148
+ ### B2-Task3: final-verification 프로필 + agents/SKILL.md + label 일반화
149
+
150
+ **Files:**
151
+ - Modify: `prompts/profiles/final-verification.md`
152
+ - Modify: `agents/SKILL.md`
153
+ - Modify: `prompts/wizard/prompts.ko.json`
154
+
155
+ - [ ] **Step 1: final-verification 프로필에 critic opt-in 선언**
156
+
157
+ `prompts/profiles/final-verification.md` 의 `- Non-goals:` 줄(현 47) **앞에** 새 top-level bullet 삽입:
158
+
159
+ ```markdown
160
+ - Cross-verification mode:
161
+ - **Acceptance critic (opt-in)**: when `convergence.critic.enabled=true` (chosen via the okstra-run picker or `--critic`), a reused-worker **acceptance devil's-advocate** pass runs after convergence to surface candidate acceptance blockers the verifiers may have missed. Each candidate is verified **confirm-or-downgrade**: confirmed → an `Acceptance Blockers` row (which, since `accepted` requires zero blockers, moves the verdict to `conditional-accept` / `blocked`); unconfirmed → a `Residual Risk` row (never dropped). See `skills/okstra-convergence/SKILL.md` "Acceptance critic pass (final-verification)".
162
+ ```
163
+
164
+ - [ ] **Step 2: agents/SKILL.md — Phase 5.6 행이 final-verification 도 커버함을 명시**
165
+
166
+ `agents/SKILL.md` 의 `| 5.6 Coverage critic | ... |` 행을 찾아, 설명 셀을 final-verification 모드도 포함하도록 보강. 행 텍스트를:
167
+ ```markdown
168
+ | 5.6 Critic pass | (opt-in) reused-worker critic pass: coverage gaps (discovery/error-analysis/impl-planning) or acceptance devil's-advocate (final-verification), each verified one round | `okstra-convergence` "Coverage critic pass" / "Acceptance critic pass" |
169
+ ```
170
+ 로 교체(컬럼 수 유지). PROGRESS 라인(`phase-5.6-critic ...`)은 그대로 두되, 그 설명에 final-verification 도 포함됨을 한 구절 덧붙여도 좋다(선택).
171
+
172
+ - [ ] **Step 3: prompts.ko.json critic_pick label 일반화**
173
+
174
+ `prompts/wizard/prompts.ko.json` 의 `critic_pick.label` 이 현재 coverage 중심 문구라면 phase-중립으로 일반화:
175
+ ```json
176
+ "label": "추가 critic 패스를 돌릴까요? (놓친 finding/blocker 를 캐는 검증 패스 — opt-in)",
177
+ ```
178
+ (label 만 변경; options/echo_template 불변.)
179
+
180
+ - [ ] **Step 4: 빌드 + 검증**
181
+
182
+ Run: `npm run build && bash validators/validate-workflow.sh`
183
+ Expected: build 성공, validator PASS.
184
+
185
+ - [ ] **Step 5: 커밋**
186
+
187
+ ```bash
188
+ git add prompts/profiles/final-verification.md agents/SKILL.md prompts/wizard/prompts.ko.json runtime/
189
+ git commit -m "feat(profiles,agents): declare acceptance devil's-advocate critic for final-verification"
190
+ ```
191
+
192
+ ---
193
+
194
+ ### B2-Task4: CHANGES + 전체 검증 + 최종 커밋
195
+
196
+ **Files:**
197
+ - Modify: `CHANGES.md`
198
+
199
+ - [ ] **Step 1: CHANGES.md 항목 추가**
200
+
201
+ `## 2026-06-05` 날짜 헤더가 있으면 그 다음에, 없으면 파일 맨 위 `# 변경 이력` 설명 단락 다음에 새 `## 2026-06-05` 헤더를 만들고 그 아래에 삽입:
202
+
203
+ ```markdown
204
+ ### feat(convergence): final-verification 악마의 변호인 critic — 놓친 acceptance blocker 발굴
205
+
206
+ - B1 의 opt-in critic 을 final-verification 으로 확장했다. 단, final-verification 의 finding 은 결함/blocker 이라 B1 의 적대적-drop 검증을 그대로 쓰면 "재현 못 한 진짜 결함" 을 억누른다. 그래서 여기서 critic 은 **악마의 변호인** — "받아들이면 안 되는 이유/놓친 acceptance blocker" 를 캐고 — 후보 blocker 는 **confirm-or-downgrade** 로 검증한다: analyser 가 확인하면 Acceptance Blocker 로 승격(→ `accepted` 는 blocker 0 을 요구하므로 verdict 가 conditional-accept/blocked 로 밀림), 확인 못 하면 **기각이 아니라 Residual Risk** 로 강등(추적 보존). 선택 UX·`--critic`·모델은 B1 그대로이고 적용 phase 에 final-verification 만 추가(이제 4 phase). 기본 off.
207
+ - 사용자 영향: 다음 release + `npx -y okstra@latest install` 후 적용. 이제 final-verification 에서도 critic 을 켜면 검증자가 놓친 수용 차단 사유를 한 번 더 캐고, 확인된 것만 verdict 를 막고 미확인은 잔여 위험으로 남는다(기본 꺼짐). 새 verdict/validator 추가 없이 기존 verdict↔blocker 규칙으로 동작한다. critic *행동*은 lead/워커 prompt 지시(LLM).
208
+ ```
209
+
210
+ - [ ] **Step 2: 전체 검증**
211
+
212
+ Run:
213
+ ```bash
214
+ npm run build
215
+ python3 -m pytest tests/ -q
216
+ bash validators/validate-workflow.sh
217
+ node bin/okstra --version
218
+ ```
219
+ ALL must pass. `tests/test_okstra_worktree.py` 의 알려진 간헐 flake 는 단독 재실행으로 확인. 그 외 실패 → STOP + BLOCKED.
220
+
221
+ - [ ] **Step 3: 일관성 self-review**
222
+
223
+ Run:
224
+ ```bash
225
+ grep -rn "final-verification" scripts/okstra_ctl/render.py scripts/okstra_ctl/wizard.py | grep -i critic
226
+ grep -rn "Acceptance critic\|acceptance-devils-advocate\|devil's-advocate\|confirm-or-downgrade" skills/okstra-convergence/SKILL.md prompts/profiles/final-verification.md CHANGES.md
227
+ ```
228
+ Confirm: render/wizard 가 final-verification 을 critic 적용 phase 로 포함; skill·profile·CHANGES 가 악마의 변호인/confirm-or-downgrade 를 일관되게 기술. 댕글링 없음.
229
+
230
+ - [ ] **Step 4: 최종 커밋**
231
+
232
+ ```bash
233
+ git add CHANGES.md
234
+ git commit -m "docs(changes): log acceptance devil's-advocate critic for final-verification"
235
+ ```
236
+
237
+ ---
238
+
239
+ ## Self-Review (작성자 체크리스트)
240
+
241
+ **1. Spec coverage**
242
+ - §2.1 적용 phase 확장 → B2-Task1(render+wizard) + B2-Task3(profile).
243
+ - §2.2 악마의 변호인 모드(프롬프트·confirm-or-downgrade·verdict 연동) → B2-Task2(skill) + B2-Task3(profile 선언).
244
+ - §2.3 상태 기록(config.critic mode/counts) → B2-Task2 State 단락.
245
+ - §3 변경 파일 9종 → B2-Task1–4 전부.
246
+ - §4 enforcement(render/wizard 테스트 + 기존 verdict validator 재사용) → B2-Task1 테스트, B2-Task2 명시.
247
+ - §6 수용기준 1–5 → B2-Task1(1,2), B2-Task2(3), B2-Task3(4), B2-Task4(5).
248
+
249
+ **2. Placeholder scan:** 코드/markdown 블록 실제 내용. TBD/TODO 없음.
250
+
251
+ **3. 식별자 일관성:** `critic_phases`(render set), `S_CRITIC_PICK`(wizard, 불변), `convergence.critic`(B1, 불변), "Acceptance critic pass (final-verification)"(신규 절 제목, Task2 정의 → Task3 profile 참조 동일), `confirm-or-downgrade`/`acceptance-devils-advocate`(skill+CHANGES 동일 철자). 4 적용 phase 집합이 render(set)·wizard(applies+summary)에서 동일.
@@ -0,0 +1,90 @@
1
+ # 적대적 검증을 implementation-planning 으로 확장 — 설계 (sub-project A)
2
+
3
+ - 작성일: 2026-06-04
4
+ - 범위: `implementation-planning` phase 의 두 교차검증 라운드를 적대적으로 전환한다 — (1) Phase 5.5 **finding convergence** 는 `requirements-discovery` / `error-analysis` 에 이미 적용된 적대적 기계를 *그대로 재사용*하고, (2) **plan-body verification** 은 검증 *자세*(입증 책임을 계획 쪽으로, 인용 경로 적극 추적)를 적대적으로 바꾸되 게이트 임계값은 **과반 유지**한다.
5
+ - 비범위
6
+ - plan-body 게이트 임계값 변경 없음 — `majority-disagree` 차단 규칙·gate-result 4값 enum·`validators/validate-run.py` 의 `validate_phase_boundary` 모두 불변. (단일-DISAGREE 차단은 사용자가 명시적으로 기각함.)
7
+ - finding convergence 의 적대적 알고리즘 자체는 재정의하지 않는다 — 이미 [`skills/okstra-convergence/SKILL.md`](../../../skills/okstra-convergence/SKILL.md) §"Adversarial Verification Mode" 에 정의돼 있고, implementation-planning 은 플래그만 켜서 상속한다.
8
+ - `final-verification` 적대화 / coverage critic 워커는 **sub-project B** 의 범위 — 본 문서에서 다루지 않는다.
9
+ - 새 verdict kind·classification 값·worker 역할 추가 없음.
10
+ - 관계: 이전 작업 [`2026-06-04-adversarial-verification-design.md`](2026-06-04-adversarial-verification-design.md) 가 만든 적대적 convergence 기계를 한 phase 더(`implementation-planning`)로 확장한다. 그 문서의 모드 분기(`convergence.adversarial`)·verdict 매핑·`disagreeBasis`·집계 규칙을 재사용한다.
11
+
12
+ ## 1. 동기
13
+
14
+ `implementation-planning` 은 거짓 합의 비용이 가장 큰 지점 중 하나다 — 여기서 통과한 계획이 곧바로 `implementation` run 의 실행 대상이 된다. 그런데 현재 이 phase 의 두 검증 라운드는 모두 협조적이다:
15
+
16
+ 1. **finding convergence** 는 `convergence.adversarial=false` 라 lightweight 협조적 — 약한 요구-갭/리스크 주장이 적극 반박 없이 `full-consensus` 로 남는다 ([`scripts/okstra_ctl/render.py:917`](../../../scripts/okstra_ctl/render.py)).
17
+ 2. **plan-body verification** 은 "이 항목이 깨졌나?" 를 묻되 입증 책임이 반박자 쪽에 있고, `dissent-isolated`(1명만 DISAGREE)는 무사통과한다 ([`skills/okstra-convergence/SKILL.md`](../../../skills/okstra-convergence/SKILL.md) §"Plan-body verification mode"). 깨진 계획 항목을 적극적으로 추적·검증하는 압력이 약하다.
18
+
19
+ → 두 라운드 모두 검증 자세를 "동의" 에서 "반박 시도" 로 뒤집되, plan-body 의 *게이트 산술*은 과반을 유지해 한 명의 오탐이 approval 을 막지 않게 한다.
20
+
21
+ ## 2. 핵심 설계
22
+
23
+ ### 2.1 finding convergence — 기존 적대 기계 재사용
24
+
25
+ `scripts/okstra_ctl/render.py` 의 `_build_convergence_block` 가 쓰는 `adversarial_phases` 집합에 `implementation-planning` 을 추가한다 ([`render.py:917`](../../../scripts/okstra_ctl/render.py)). 이 한 줄로 implementation-planning 의 finding convergence 는:
26
+
27
+ - `config.adversarial=true`, `config.verificationMode="full-reanalysis"` 를 주입받고,
28
+ - §"Adversarial Verification Mode" 의 적대적 프롬프트·집계(1 counter-evidence→강등·burden-not-met)·`disagreeBasis`·인용-증거-한정 재조사를 *그대로* 상속한다.
29
+
30
+ `maxRounds` 는 현행 유지(implementation-planning=2). 적대적 full-reanalysis 가 라운드당 비용을 올리지만 재조사 범위가 finding 인용 증거로 한정돼 있어 폭증하지 않으며, 계획 단계의 높은 비용-정당성이 이를 흡수한다.
31
+
32
+ ### 2.2 plan-body verification — 적대적 자세, 과반 게이트 유지
33
+
34
+ plan-body verification 라운드는 같은 run 의 `config.adversarial` 플래그를 읽어 적대적 자세를 적용한다(별도 플래그 없음 — 한 run 의 적대성은 단일 스위치).
35
+
36
+ **바뀌는 것 (프롬프트/자세):**
37
+ - 검증자는 각 `P-*` 항목의 인용 경로/심볼/명령을 *직접 열어* 실존·실행성을 확인하며 항목을 깨뜨리려 시도한다. (현행 plan-body 프롬프트의 "원본 재분석 금지" 는 유지하되, *항목이 인용한 경로/명령의 실존 확인*은 허용하도록 좁게 푼다.)
38
+ - 입증 책임은 계획 쪽에 있다 — 인용 경로/명령/검증신호를 확인할 수 없으면 해당 `DISAGREE(<kind>)`(a–e 중 적용 가능한 것)로 응답한다.
39
+ - plan-body 는 lightweight 전용 그대로다(full-reanalysis 강제 안 함) — "인용 경로 실존 확인" 이 적대적 재조사의 범위.
40
+
41
+ **바뀌지 않는 것 (게이트 산술):**
42
+ - classification enum(`full-consensus | partial-consensus | dissent-isolated | majority-disagree | contested`) 불변.
43
+ - 게이트 규칙 불변 — `majority-disagree`(분석자 **과반** DISAGREE) 일 때만 approval 차단. `dissent-isolated`(1명 DISAGREE)는 종전대로 partial-consensus 로 처리하며 차단하지 않는다.
44
+ - gate-result 4값(`passed` / `passed-with-dissent` / `blocked-by-disagreement` / `aborted-non-result`)·`validate_phase_boundary`([`validators/validate-run.py:1180`](../../../validators/validate-run.py)) 불변.
45
+
46
+ 즉 plan-body 적대화는 검증의 *질*(능동적 추적·계획 측 입증책임)만 올리고 *판정 임계값*은 그대로다. 이건 LLM 프롬프트 지시 레이어의 변경이며 런타임으로 강제되지 않는다(§4).
47
+
48
+ ### 2.3 일관성 sweep (Rule 2 — "어떤 phase 가 적대적인가" 가 적히는 모든 곳)
49
+
50
+ `adversarial_phases` 집합이 행동 SSOT 다. 적대적 phase 목록을 *열거*하는 문서 지점을 모두 2개→3개로 갱신한다:
51
+
52
+ - [`render.py:906`](../../../scripts/okstra_ctl/render.py) docstring 의 default 설명
53
+ - [`skills/okstra-convergence/SKILL.md:49`](../../../skills/okstra-convergence/SKILL.md) Configuration 표 `adversarial` 행
54
+ - 같은 파일 §"Adversarial Verification Mode" 전제 문장(현 line 198 부근)
55
+ - 같은 파일 Schema rules 의 `config.adversarial` 불릿("default for requirements-discovery / error-analysis")
56
+ - [`prompts/profiles/_common-contract.md:17`](../../../prompts/profiles/_common-contract.md) Phase 5.5 문장
57
+ - [`agents/SKILL.md`](../../../agents/SKILL.md) `convergence.adversarial` knob 행
58
+
59
+ §"Scoped full-reanalysis" 비용-근거 산문(현 line 204·302)의 "requirements-discovery·error-analysis 가 가장 큰 회피 가능 비용" 서술은 *비용 출처 설명*이므로 implementation-planning 을 같은 맥락에 더해 갱신한다.
60
+
61
+ ## 3. 변경 파일
62
+
63
+ 1. [`scripts/okstra_ctl/render.py`](../../../scripts/okstra_ctl/render.py) `_build_convergence_block` — `adversarial_phases` 집합에 `implementation-planning` 추가 + docstring 갱신.
64
+ 2. [`tests/test_render_convergence_adversarial.py`](../../../tests/test_render_convergence_adversarial.py) — `implementation-planning` 을 비적대 파라미터 목록에서 적대 파라미터 목록으로 **이동**.
65
+ 3. [`tests/test_plan_body_verification.py`](../../../tests/test_plan_body_verification.py) — `test_convergence_block_default_for_implementation_planning` 의 기대값을 `adversarial: True`, `verificationMode: "full-reanalysis"` 로 갱신.
66
+ 4. [`skills/okstra-convergence/SKILL.md`](../../../skills/okstra-convergence/SKILL.md) — (a) §2.3 의 phase-목록 갱신 3곳, (b) §"Plan-body verification mode" 에 "Adversarial plan-body posture" 하위 절 추가(자세/프롬프트 변경 + 게이트 과반 유지 명시).
67
+ 5. [`prompts/profiles/implementation-planning.md`](../../../prompts/profiles/implementation-planning.md) — Phase 5.5 finding convergence 적대 선언 + §4.5.9 plan-body 적대 자세 선언.
68
+ 6. [`prompts/profiles/_common-contract.md`](../../../prompts/profiles/_common-contract.md) — §2.3 Phase 5.5 문장 갱신.
69
+ 7. [`agents/SKILL.md`](../../../agents/SKILL.md) — §2.3 knob 행 갱신.
70
+ 8. [`CHANGES.md`](../../../CHANGES.md) — `사용자 영향:` 항목.
71
+
72
+ ## 4. Enforcement — 선언과 강제의 구분
73
+
74
+ - **machine-강제 (변경됨):** `render.py` 가 implementation-planning 에 `adversarial=true` + `full-reanalysis` 를 주입 — 기존 contract 테스트([`tests/test_convergence_state_contract.py`](../../../tests/test_convergence_state_contract.py)) + render 테스트가 형태를 강제. 본 sub-project 는 render 테스트 + plan-body 블록 테스트의 기대값을 갱신해 이를 반영한다.
75
+ - **machine-강제 (불변):** plan-body 의 gate↔approval 대응은 `validate_phase_boundary` 가 종전대로 강제. 게이트 enum·classification enum 불변이라 validator 코드 변경 없음.
76
+ - **prompt-only (강제 불가):** finding 의 적대적 반박 행동, plan-body 의 능동적 경로 추적·계획측 입증책임은 lead/워커(LLM) 프롬프트 지시일 뿐 런타임 강제가 아니다. skill·profile 선언으로만 유도한다.
77
+
78
+ ## 5. 비용·리스크
79
+
80
+ - **비용:** finding convergence 가 lightweight→full-reanalysis(scoped) + 적대적, 최대 2라운드. 재조사 범위 한정으로 폭증 억제. plan-body 는 lightweight 유지라 추가 비용은 인용 경로 확인 정도.
81
+ - **리스크 — 계획 finding 거짓 강등:** 적대적 finding convergence 가 참인 요구-갭 주장을 `contested` 로 강등할 수 있음. 완화: `contested` 는 기각이 아니라 "다툼 있음" 분류로 리포트에 남고, counter-evidence 반박은 `file:line` 인용 필수라 사유 추적 가능(기존 기계의 성질 그대로).
82
+ - **리스크 — plan-body 오탐:** 게이트 과반 유지로 1명의 오탐이 approval 을 막지 않음(사용자 결정). 능동적 추적이 늘려도 차단은 과반 합의가 필요.
83
+
84
+ ## 6. 수용 기준
85
+
86
+ 1. `implementation-planning` 의 manifest `convergence` 블록에 `adversarial: true`, `verificationMode: "full-reanalysis"` 가 주입된다. (3개 적대 phase: requirements-discovery / error-analysis / implementation-planning.)
87
+ 2. convergence skill 이 plan-body 적대적 자세를 정의하고, 게이트가 과반 유지임을 명시한다.
88
+ 3. 적대 phase 목록이 적히는 모든 문서 지점(§2.3)이 3개로 일치한다(grep 일관성).
89
+ 4. `python3 -m pytest tests/` + `bash validators/validate-workflow.sh` 통과.
90
+ 5. implementation-planning 프로필이 Phase 5.5 적대 + plan-body 적대 자세를 선언한다.
@@ -0,0 +1,176 @@
1
+ # Phase 5.5 적대적 검증 (adversarial verification) — 설계
2
+
3
+ - 작성일: 2026-06-04
4
+ - 범위: `requirements-discovery` / `error-analysis` 두 phase 의 **Phase 5.5 convergence 재검증**을, 검증자가 다른 워커의 주장을 적극적으로 반박(refute)하려 시도하고 입증 책임을 주장 쪽에 두는 **적대적 검증** 구조로 전환한다. 별도 검증자 에이전트나 새 스테이지를 만들지 않고, 기존 convergence 재검증 루프를 phase-조건부 적대적 모드로 재구성한다.
5
+ - 비범위
6
+ - 신규 worker/agent 추가 없음. `requirements-discovery` / `error-analysis` 의 `Required workers:` 로스터 불변.
7
+ - `implementation-planning` / `implementation` / `final-verification` / `release-handoff` 의 convergence 동작 불변 — 이들은 현행 협조적(collaborative) 재검증을 그대로 유지한다.
8
+ - `implementation-planning` 의 plan-body verification(`P-*` 큐) 불변 — 본 설계는 finding 큐(`F-*`)만 다룬다.
9
+ - convergence 라운드/큐 구조 자체(Round 0 grouping, queue-pruned 루프, Round 2 gate)는 그대로 재사용한다.
10
+ - 관계: 본 문서는 [`skills/okstra-convergence/SKILL.md`](../../../skills/okstra-convergence/SKILL.md) 의 §"Verification Mode" 와 §"Lightweight Re-verification Prompt" 를 **두 phase 에 한해** 적대적 변형으로 확장한다. 협조적 모드 정의는 다른 phase 를 위해 그대로 남는다.
11
+
12
+ ## 1. 동기 — 현재 재검증은 협조적이라 거짓 합의를 만든다
13
+
14
+ 현재 Phase 5.5 의 재검증은 본질적으로 "동의 기본값" 구조다.
15
+
16
+ 1. **프롬프트가 협조적이다.** lightweight reverify 프롬프트([`skills/okstra-convergence/SKILL.md:247`](../../../skills/okstra-convergence/SKILL.md)) 는 `AGREE / DISAGREE / SUPPLEMENT` 를 묻고, "제시된 증거에 기반해 유효한가" 를 판단하게 한다. 적극적으로 깨뜨리라는 압력이 없으므로 AGREE 가 저비용 기본값이 된다.
17
+ 2. **집계가 반박자에게 입증 책임을 지운다.** 집계 규칙([`skills/okstra-convergence/SKILL.md:120`](../../../skills/okstra-convergence/SKILL.md)) 은 "다수가 AGREE → consensus" 다. 즉 주장은 다수가 적극적으로 반박해야만 강등된다. 틀린 주장이라도 아무도 적극 반박하지 않으면 `full-consensus` 로 살아남는다.
18
+ 3. **lightweight 는 텍스트만 본다.** 검증자는 원본 코드/로그를 재조사하지 않고 "제시된 증거"만 본다([`skills/okstra-convergence/SKILL.md:183`](../../../skills/okstra-convergence/SKILL.md)). 잘못된 증거 인용이 그대로 통과한다.
19
+
20
+ 특히 `requirements-discovery`(라우팅 결정)와 `error-analysis`(근본 원인 분석)는 **틀린 주장이 다음 phase 전체를 오도**하는 지점이다. 이 두 phase 에서 거짓 합의의 비용이 가장 크다. 따라서 검증의 기본 자세를 "동의" 에서 "반박 시도" 로 뒤집는다.
21
+
22
+ ## 2. 핵심 원칙
23
+
24
+ ### 2.1 phase-조건부 적대적 모드
25
+
26
+ 적대적 검증은 **`requirements-discovery` 와 `error-analysis` 두 phase 에만** 적용한다. convergence skill 은 모든 phase 가 공유하므로, 모드 분기는 manifest 의 `convergence` 블록에 새 플래그로 표현한다.
27
+
28
+ | 키 | 두 적대적 phase 기본값 | 그 외 phase 기본값 |
29
+ |---|---|---|
30
+ | `convergence.adversarial` | `true` | `false` |
31
+ | `convergence.verificationMode` | `"full-reanalysis"` | `"lightweight"` |
32
+ | `convergence.maxRounds` | req-discovery=`1`, error-analysis=`2` (현행 유지) | 현행 유지 |
33
+
34
+ 이 기본값은 [`scripts/okstra_ctl/render.py:899`](../../../scripts/okstra_ctl/render.py) `_build_convergence_block` 가 주입한다. 기존 `maxRounds` 의 phase-aware 분기(`1 if requirements-discovery else 2`) 와 동일한 패턴을 따른다. manifest 가 키를 명시하면 그 값을 우선한다(다른 phase 에서 적대적 검증을 실험적으로 켜는 것은 manifest override 로 가능 — 그러나 기본값으로 권하지 않는다).
35
+
36
+ `adversarial=false` 이면 본 설계의 모든 변경은 비활성이고 현행 협조적 동작이 그대로 돈다.
37
+
38
+ ### 2.2 적대적 재검증 프롬프트 — 반박이 임무다
39
+
40
+ `adversarial=true` 일 때 lead 는 §"Lightweight Re-verification Prompt" 대신 **적대적 프롬프트**를 사용한다. 핵심 지시:
41
+
42
+ - "너의 임무는 이 주장을 **깨뜨리는 것**이다. 인용된 원본 증거를 직접 열어 재조사하고, 주장을 무너뜨릴 반대 증거를 적극적으로 찾아라."
43
+ - verdict 라벨(프롬프트 표면):
44
+ - **REFUTED** — 주장을 반박했다. 반드시 근거를 댄다(아래 `disagreeBasis`).
45
+ - **SURVIVES** — 적극적으로 반박을 시도했으나 깨지 못했다. 주장이 공격을 견뎠다.
46
+ - **SURVIVES-WITH-CAVEAT** — 견디나 범위 한정/추가 조건/전제를 발견했다.
47
+ - **불확실성 처리(BLOCKING):** 원본 증거를 재조사한 뒤에도 주장을 **확인할 수도, 반증할 수도 없으면** 기본 verdict 는 **REFUTED** 다(`disagreeBasis = burden-not-met`). 입증 책임은 주장 쪽에 있으므로, 스스로 입증되지 않은 주장은 살아남지 못한다.
48
+
49
+ ### 2.3 verdict 매핑 — 영속 enum 불변, 신규 필드로 적대성 기록
50
+
51
+ 상태 아티팩트의 `verdict` enum 은 `{agree, disagree, supplement, verification-error}` 를 **그대로 유지**한다(contract 테스트 enum 변경 최소화). 프롬프트 라벨은 아래로 매핑해 영속한다:
52
+
53
+ | 프롬프트 라벨 | 영속 `verdict` |
54
+ |---|---|
55
+ | SURVIVES | `agree` |
56
+ | SURVIVES-WITH-CAVEAT | `supplement` |
57
+ | REFUTED | `disagree` |
58
+
59
+ 적대성의 핵심 정보는 vote 에 추가하는 신규 필드 **`disagreeBasis`** 로 기록한다:
60
+
61
+ | 값 | 의미 |
62
+ |---|---|
63
+ | `counter-evidence` | 반대 증거를 `file:line`(또는 로그 라인)으로 인용한 **강한 반박**. 인용은 `votes.<worker>.explanation` 에 포함한다. |
64
+ | `burden-not-met` | 재조사했으나 확인도 반증도 못 함 → 주장이 입증 책임을 다하지 못함(= "불확실하면 기각"). |
65
+ | `null` | verdict 가 `disagree` 가 아닐 때(=agree/supplement/verification-error). |
66
+
67
+ `adversarial=true` 인데 verdict 가 `disagree` 이고 `disagreeBasis` 가 null 이면 contract 위반이다(§5 참조). 즉 적대적 모드의 모든 반박은 둘 중 하나의 근거를 반드시 가진다 — 근거 없는 "그냥 반대" 는 허용하지 않는다.
68
+
69
+ ### 2.4 적대적 집계 규칙 — 입증 책임을 주장 쪽으로
70
+
71
+ `adversarial=true` 일 때 §"Convergence Algorithm" 의 분류 로직을 다음으로 대체한다(협조적 모드 로직은 `adversarial=false` 에서 그대로). 한 finding `F` 에 대해, `verification-error` 표는 분자·분모 모두에서 제외한다(현행과 동일):
72
+
73
+ ```text
74
+ disagrees = [v for v in non-error votes if v.verdict == "disagree"]
75
+ hard_refutes = [v for v in disagrees if v.disagreeBasis == "counter-evidence"]
76
+
77
+ IF len(disagrees) == 0:
78
+ # 아무도 깨지 못함 → 주장이 공격을 견딤
79
+ F.classification = "full-consensus"
80
+ (단, supplement(=caveat)가 있으면 "partial-consensus")
81
+ ELIF len(hard_refutes) >= 1:
82
+ # 증거 기반 반박이 1건이라도 성립 → 즉시 강등 (다수결 무관)
83
+ IF 비-발견자 전원이 disagree:
84
+ F.classification = "worker-unique" # 사실상 기각
85
+ ELSE:
86
+ F.classification = "contested"
87
+ ELSE:
88
+ # disagree 는 있으나 전부 burden-not-met (강한 반박 0건)
89
+ IF 비-발견자 전원이 disagree:
90
+ F.classification = "worker-unique"
91
+ ELIF burden-not-met disagree 가 다수(비-error 표의 과반):
92
+ F.classification = "contested"
93
+ ELSE:
94
+ F.classification = "partial-consensus" # 소수의 약한 의심 — 견딘 것으로 본다
95
+ ```
96
+
97
+ 설계 의도:
98
+ - **`counter-evidence` 반박 1건 = 강등.** 사용자가 명시한 "증거 기반 반박이 1건이라도 성립하면 강등". 다수가 동의해도 누군가 반대 증거를 `file:line` 으로 제시하면 그 주장은 무조건 `contested` 이상으로 내려간다.
99
+ - **`burden-not-met` 은 다수일 때만 강등.** 한 검증자가 "잘 모르겠다" 한 것만으로 주장을 죽이지는 않되, 과반이 입증 실패를 보고하면 주장은 입증 책임을 못 다한 것으로 강등한다. 이로써 "불확실하면 기각 쪽으로 기운다" 를 구현한다.
100
+ - 반박의 두 종류를 구분 영속하므로, 최종 리포트에서 "왜 강등됐는가"(반대 증거 발견 vs 입증 실패)를 추적할 수 있다.
101
+
102
+ multi-라운드(error-analysis maxRounds=2)에서 라운드 간 carry-forward·최종 분류는 현행 규칙을 그대로 따르되, 각 라운드의 분류 판정에 위 적대적 로직을 적용한다.
103
+
104
+ ### 2.5 full-reanalysis 의 범위 한정 — 비용 폭증 방지
105
+
106
+ 선택된 `verificationMode="full-reanalysis"` 는 검증자가 원본 증거를 직접 재조사하게 한다. 그러나 [`skills/okstra-convergence/SKILL.md:245`](../../../skills/okstra-convergence/SKILL.md) 는 lightweight 를 "requirements-discovery·error-analysis 에서 가장 큰 회피 가능 비용" 이라 명시한다. 전체 instruction-set 재독으로 회귀하면 이 비용을 정면으로 되살린다.
107
+
108
+ **해소:** 적대적 full-reanalysis 의 재조사 범위를 **"해당 finding 이 인용한 증거 파일 + 그 인접부"로 한정**한다. 전체 task brief / instruction-set / `final-report-template.md` 재독은 금지한다. 즉 검증자는 공격 대상 주장이 가리키는 코드/로그만 직접 열어 반대 증거를 찾는다.
109
+
110
+ - §"Reverify prompt: required-reading suppression (BLOCKING)" 의 full-reanalysis 분기를 적대적 모드용으로 좁힌다: analysis-worker 파일 목록 전체가 아니라 **인용된 증거 경로만** 주입한다.
111
+ - maxRounds 는 현행 유지(req-discovery=1, error-analysis=2). 적대적 1라운드면 "한 번 깨뜨려 보기" 에 충분하고, 비용을 라운드 수로 곱하지 않는다.
112
+
113
+ ## 3. 데이터 모델
114
+
115
+ ### 3.1 convergence 상태 아티팩트 (`runs/<task-type>/state/convergence-<task-type>-<seq>.json`)
116
+
117
+ - `schemaVersion` 을 `"1.2"` 로 올린다. reader 는 `"1.0"`/`"1.1"` 을 계속 수용하고 누락 필드는 `null` 로 취급한다.
118
+ - `config` 에 신규 키 추가:
119
+ - `adversarial`: boolean. 이 run 이 적대적 모드였는지. 현행 두 적대적 phase 는 `true`.
120
+ - `findings[].rounds[].votes.<worker>` 에 신규 키 추가:
121
+ - `disagreeBasis`: enum `counter-evidence | burden-not-met | null`. §2.3 의 규칙을 따른다.
122
+ - 기존 필드(`verdict` enum, `classification` enum, `finalState` 등)는 불변.
123
+
124
+ ### 3.2 render.py 가 주입하는 manifest `convergence` 블록
125
+
126
+ `_build_convergence_block`([`scripts/okstra_ctl/render.py:899`](../../../scripts/okstra_ctl/render.py)) 가 다음을 추가로 결정한다:
127
+
128
+ ```python
129
+ adversarial_phases = {"requirements-discovery", "error-analysis"}
130
+ is_adversarial = task_type in adversarial_phases
131
+ # ...
132
+ "adversarial": is_adversarial,
133
+ "verificationMode": "full-reanalysis" if is_adversarial else "lightweight",
134
+ ```
135
+
136
+ `maxRounds` 의 기존 분기는 그대로 둔다.
137
+
138
+ ## 4. 변경 대상 파일 (모두 source — `runtime/` 직접 수정 없음)
139
+
140
+ 1. [`skills/okstra-convergence/SKILL.md`](../../../skills/okstra-convergence/SKILL.md)
141
+ - §"Configuration" 표에 `adversarial` 키 추가, 두 phase 기본값 명시.
142
+ - §"Verification Mode" 에 적대적 모드 설명 추가(범위 한정 full-reanalysis 포함).
143
+ - §"Convergence Algorithm" 에 `adversarial=true` 분기 집계 로직(§2.4) 추가. 협조적 로직은 그대로 유지.
144
+ - §"Lightweight Re-verification Prompt" 옆에 "Adversarial Re-verification Prompt"(§2.2) 신설.
145
+ - §"Reverify prompt: required-reading suppression" 의 full-reanalysis 분기를 적대적 모드용 인용-증거-한정으로 좁힘.
146
+ - §"Convergence State Artifact" 스키마를 1.2 로 갱신: `config.adversarial`, `votes.<worker>.disagreeBasis`.
147
+ 2. [`scripts/okstra_ctl/render.py:899`](../../../scripts/okstra_ctl/render.py) `_build_convergence_block` — §3.2.
148
+ 3. [`prompts/profiles/requirements-discovery.md`](../../../prompts/profiles/requirements-discovery.md) + [`prompts/profiles/error-analysis.md`](../../../prompts/profiles/error-analysis.md) — Phase 5.5 가 적대적으로 돈다는 선언 1줄(프로필이 동작의 authoritative 선언처임).
149
+ 4. [`prompts/profiles/_common-contract.md:16`](../../../prompts/profiles/_common-contract.md) — "Worker interaction model" 의 Phase 5.5 설명에, 두 phase 는 적대적 peer review 라는 한 줄 추가.
150
+ 5. [`tests/test_convergence_state_contract.py`](../../../tests/test_convergence_state_contract.py) + `tests/fixtures/convergence/` — `1.2` 수용, `disagreeBasis` enum 검증, `config.adversarial` 존재 검증, 적대적 fixture 1개 추가(`counter-evidence` 반박 1건 → `contested` 케이스).
151
+ 6. [`CHANGES.md`](../../../CHANGES.md) — `사용자 영향:` 항목.
152
+
153
+ ## 5. Enforcement — 선언과 강제의 구분
154
+
155
+ 정직한 enforcement 경계:
156
+
157
+ - **적대적 *행동* 자체(lead 가 실제로 반박을 시도했는지, 검증자가 증거를 재조사했는지)는 런타임으로 강제할 수 없다.** lead 와 워커는 LLM 이므로, 적대성은 skill/프롬프트의 선언과 지시로만 유도된다. 이 한계를 문서에 명시한다.
158
+ - **강제되는 것은 아티팩트의 *형태* 뿐이다.** `tests/test_convergence_state_contract.py` 가 fixture 에 대해 검증:
159
+ - `config.adversarial` 가 boolean 으로 존재.
160
+ - `disagreeBasis` 가 enum `{counter-evidence, burden-not-met, null}` 안에 있음.
161
+ - `adversarial==true` 인 fixture 에서, verdict 가 `disagree` 이면 `disagreeBasis != null`.
162
+ - convergence 상태는 런타임 `validators/validate-run.py` 가 검사하지 않는다(현행과 동일). 따라서 본 설계는 런타임 run 에 대한 적대성 강제를 **약속하지 않는다** — fixture contract 테스트가 유일한 자동 검증 지점이다.
163
+
164
+ ## 6. 비용·리스크
165
+
166
+ - **비용:** full-reanalysis 로의 전환은 lightweight 대비 라운드당 비용을 올린다. §2.5 의 인용-증거-한정으로 폭증을 막고, maxRounds 를 현행 유지(req-discovery=1)해 라운드 곱을 억제한다.
167
+ - **리스크 — 거짓 강등(false negative):** 적대적 모드는 참인 주장을 `contested` 로 강등할 수 있다(검증자가 잘못된 반대 증거를 제시). 완화: `counter-evidence` 반박은 반드시 `file:line` 인용을 요구하므로(§2.3), 강등 사유가 리포트에 기록되어 사용자가 추적·반박할 수 있다. `contested` 는 기각이 아니라 "다툼 있음" 분류이므로 finding 은 리포트에 남는다.
168
+ - **리스크 — burden-not-met 남용:** 검증자가 게으르게 "잘 모르겠다" 로 일관하면 다수 burden-not-met 으로 멀쩡한 주장이 강등될 수 있다. 완화: 프롬프트가 "재조사 후" 에만 burden-not-met 을 허용하도록 지시하고, 단일 burden-not-met 은 강등시키지 않는다(과반 필요, §2.4).
169
+
170
+ ## 7. 수용 기준
171
+
172
+ 1. `requirements-discovery` / `error-analysis` 의 manifest `convergence` 블록에 `adversarial: true`, `verificationMode: "full-reanalysis"` 가 주입된다. 그 외 phase 는 `adversarial: false`, `lightweight` 유지.
173
+ 2. convergence skill 이 `adversarial=true` 분기에서 적대적 프롬프트·적대적 집계·인용-증거-한정 재조사를 정의한다. `adversarial=false` 동작은 byte 단위로 현행과 동일.
174
+ 3. 상태 스키마 1.2 가 `config.adversarial` 와 `votes.<worker>.disagreeBasis` 를 문서화하고, contract 테스트가 §5 의 형태 규칙을 강제한다.
175
+ 4. `python3 -m pytest tests/` 와 `bash validators/validate-workflow.sh` 통과.
176
+ 5. 두 프로필과 `_common-contract.md` 가 적대적 Phase 5.5 를 선언한다.