okstra 0.47.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,99 @@
1
+ # Coverage critic — 설계 (sub-project B1)
2
+
3
+ - 작성일: 2026-06-04
4
+ - 범위: `requirements-discovery` / `error-analysis` / `implementation-planning` 세 phase 에, Phase 5.5 convergence 직후·Phase 6 report-writer 직전에 도는 **coverage critic pass** 를 추가한다. critic 은 기존 worker subagent 를 *재사용*해 dispatch 되며("아무도 안 본 각도/파일/모달리티, finding 없는 요구사항을 내라"), critic 이 낸 coverage gap 은 **1회 적대적 reverify** 를 거쳐 살아남은 것만 최종 finding/clarification 으로 병합된다. 사용 여부·backing provider 는 **okstra-run 초기 select box** 로 고르며(추천 + "직접 입력"), CLI 파라미터가 넘어오면 그 선택 단계를 건너뛴다. 기본값은 off(opt-in).
5
+ - 비범위
6
+ - `final-verification` 의 verdict 악마의 변호인은 **sub-project B2** — 본 문서 밖. (critic primitive 는 B2 가 재사용한다.)
7
+ - 새 설치형 agent(`critic-worker.md` / `installed-agents.json`) 추가 없음 — 기존 `claude-worker`/`codex-worker`/`gemini-worker` subagent_type 을 critic 프롬프트로 재사용.
8
+ - convergence 의 finding/plan-body classification·gate enum 변경 없음 — critic gap 은 기존 adversarial finding classifier 를 그대로 탄다.
9
+ - 워커 *수*(로스터 크기) 변경 없음 — critic 은 별도 pass 이지 Phase 4 analyser 로스터를 늘리는 게 아니다.
10
+ - 관계: 적대적 convergence 기계([`skills/okstra-convergence/SKILL.md`](../../../skills/okstra-convergence/SKILL.md) §"Adversarial Verification Mode")를 재사용해 critic gap 을 검증한다. wizard select 패턴은 기존 `okstra-run` step 들(`defaults_or_custom` 등)과 동일한 picker 관례를 따른다.
11
+
12
+ ## 1. 동기 — 커버리지 갭은 구조적이다
13
+
14
+ okstra 의 analyser 워커들은 설계상 *같은* section 1–5 질문을 답한다 — triangulation 이지 partition 이 아니다 ([`skills/okstra-team-contract/SKILL.md:204`](../../../skills/okstra-team-contract/SKILL.md)). 따라서 워커를 늘려도 *같은 종류*의 finding 을 더 찾을 뿐, "아무도 보지 않은 각도" 는 구조적으로 비어 있다. 합의 품질(b)은 적대적 검증으로 올렸지만, 커버리지(c) — 놓친 finding — 는 별도 메커니즘이 필요하다. coverage critic 은 통합 findings 를 입력으로 "무엇이 빠졌나" 만 전담해 이 갭을 메운다.
15
+
16
+ ## 2. 핵심 설계
17
+
18
+ ### 2.1 critic pass primitive
19
+
20
+ Phase 5.5 convergence 가 끝나 findings 가 분류된 직후, Phase 6 report-writer dispatch **전에** lead 가 critic pass 를 1회 실행한다.
21
+
22
+ - **재사용 dispatch:** 선택된 provider 의 기존 subagent_type(`claude-worker` / `codex-worker` / `gemini-worker`)에 critic 프롬프트로 dispatch. 새 agent 정의 없음.
23
+ - 입력: task brief + Phase 5.5 통합 findings 요약 + 코드베이스 read 접근.
24
+ - 프롬프트 골자: "다음은 이미 합의된 findings 다. 아무도 검사하지 않은 파일/디렉터리/실행경로, finding 이 하나도 없는 요구사항/수용기준, 제기됐지만 아무도 검증하지 않은 주장을 찾아라. 각 coverage gap 을 새 finding 으로, 근거(`file:line` 또는 요구사항 인용)와 함께 내라. 이미 있는 finding 을 반복하지 마라."
25
+ - 결과 파일: `runs/<task-type>/worker-results/<provider>-critic-<task-type>-<seq>.md`.
26
+ - **1회 적대적 reverify (질문3 결정):** critic 이 낸 gap 들을 `originWorker=<provider>-critic` 인 새 finding 으로 verification queue 에 넣고, **Phase 4 analyser 들**(critic 자신 제외)이 1라운드 적대적으로 reverify 한다(기존 §"Adversarial Verification Mode" classifier 재사용: 증거기반 반박 1건 → 강등). `full-consensus`/`partial-consensus` 로 살아남은 gap 만 최종 리포트 finding 으로 병합; 강등(`contested`/`worker-unique`)된 gap 은 환각으로 보고 버리거나 dissent 로만 기록.
27
+ - **모델 (질문2 결정):** backing provider 는 `--critic <claude|codex|gemini>` 로 선택, 모델은 그 provider 의 기존 `--<provider>-model` 값(executor 바인딩 패턴 미러링). 별도 critic 모델 플래그는 두지 않는다(YAGNI).
28
+
29
+ ### 2.2 선택 UX — wizard select step + CLI bypass
30
+
31
+ critic 사용 여부·provider 는 실행 파라미터 전용이 아니라 **okstra-run 초기 select box** 로 고른다.
32
+
33
+ - 신규 wizard step `S_CRITIC_PICK` — [`scripts/okstra_ctl/wizard.py`](../../../scripts/okstra_ctl/wizard.py) 의 `_build_*`/`_submit_*` + [`prompts/wizard/prompts.ko.json`](../../../prompts/wizard/prompts.ko.json) SOT. picker 관례(추천 1~2 + 마지막 "직접 입력"):
34
+ - `critic 사용 안 함 (기본·추천)` → 비활성
35
+ - `claude critic (opus)` *(추천)* → provider=claude, 모델=해당 phase 의 claude 모델
36
+ - `직접 입력` → provider(+선택 모델)를 사용자가 직접 지정
37
+ - (목록은 현재 analyser 로스터에 맞춰 codex/gemini 옵션을 추천에 추가할 수 있으나, 추천은 최대 2개 + 직접 입력 = 3옵션 관례 유지.)
38
+ - **CLI 우선·건너뛰기:** `--critic <provider>` 또는 `--no-critic` 가 넘어오면 `S_CRITIC_PICK` 을 **건너뛴다**. `okstra.sh` / `node bin/okstra` 비대화 경로는 플래그로, 대화형 `okstra-run` 은 select box 로. 플래그 무지정 + 비대화 경로면 기본 off.
39
+ - 세 진입점(okstra-run skill / okstra.sh / node CLI)은 모두 `prepare_task_bundle()` 로 수렴하므로, critic 선택은 거기서 manifest 로 직렬화된다(단일 참조점 보존).
40
+
41
+ ### 2.3 manifest `convergence.critic` 블록 + render resolve
42
+
43
+ [`scripts/okstra_ctl/render.py`](../../../scripts/okstra_ctl/render.py) 가 `convergence` 하위에 `critic` 블록을 emit 한다:
44
+
45
+ ```json
46
+ "critic": {
47
+ "enabled": false,
48
+ "provider": null,
49
+ "modelExecutionValue": null
50
+ }
51
+ ```
52
+
53
+ - `enabled`: wizard 선택 or `--critic` 플래그가 provider 를 정하면 `true`, 아니면 `false`(기본).
54
+ - `provider`: `claude` | `codex` | `gemini` | `null`.
55
+ - `modelExecutionValue`: 선택된 provider 의 모델(그 provider 의 `--<provider>-model` 시드에서). `enabled=false` 면 `null`.
56
+
57
+ lead 는 Phase 5.5 종료 시 이 블록을 읽어 critic pass 실행 여부/대상을 정한다.
58
+
59
+ ### 2.4 적용 phase
60
+
61
+ requirements-discovery / error-analysis / implementation-planning. (final-verification 은 B2.) 이 세 phase 는 모두 finding 을 산출하므로 coverage critic 이 의미가 있다. release-handoff/implementation 은 적용하지 않는다.
62
+
63
+ ## 3. 데이터 모델
64
+
65
+ - **manifest:** §2.3 의 `convergence.critic` 블록.
66
+ - **convergence 상태 아티팩트:** critic gap 은 `findings[]` 에 `originWorker: "<provider>-critic"` 로 들어가고 기존 rounds/votes/classification 스키마를 그대로 쓴다. 추적용으로 finding 에 선택 필드 `source: "critic"` 를 둔다(없으면 `null`=일반 워커 발견). schemaVersion 은 `1.2` 유지(optional 필드 추가, reader 는 누락을 null 로 취급) — enum 변경 없음.
67
+ - **convergence state `config`:** critic 실행 시 `config.critic` 에 `{ provider, modelExecutionValue, gapsProposed, gapsMerged }` 요약을 기록(감사용).
68
+
69
+ ## 4. 변경 파일
70
+
71
+ 1. [`scripts/okstra_ctl/render.py`](../../../scripts/okstra_ctl/render.py) `_build_convergence_block` — `critic` 블록 emit + `--critic`/wizard 선택 resolve.
72
+ 2. [`scripts/okstra_ctl/run.py`](../../../scripts/okstra_ctl/run.py) — `--critic <provider>` / `--no-critic` argparse + ctx 전달 + 세 phase 한정 적용.
73
+ 3. [`scripts/okstra_ctl/wizard.py`](../../../scripts/okstra_ctl/wizard.py) — `S_CRITIC_PICK` step `_build`/`_submit` + 흐름 편입(CLI 미지정 시에만 표시).
74
+ 4. [`prompts/wizard/prompts.ko.json`](../../../prompts/wizard/prompts.ko.json) (+ 영문 SOT 있으면 동기) — `critic_pick` step label/options.
75
+ 5. [`skills/okstra-convergence/SKILL.md`](../../../skills/okstra-convergence/SKILL.md) — "Coverage critic pass" 절 신설(시점·프롬프트·1회 적대 reverify·병합·`convergence.critic` 스키마).
76
+ 6. [`agents/SKILL.md`](../../../agents/SKILL.md) — Phase 5.5→6 흐름에 critic pass + `PROGRESS: phase-5.5-critic provider=<p>` 라인 + (해당 시) 모델/knob 참조.
77
+ 7. [`prompts/profiles/requirements-discovery.md`](../../../prompts/profiles/requirements-discovery.md) / [`error-analysis.md`](../../../prompts/profiles/error-analysis.md) / [`implementation-planning.md`](../../../prompts/profiles/implementation-planning.md) — coverage critic opt-in 선언 1줄.
78
+ 8. 테스트: render `critic` 블록 resolve(`--critic`/무지정/`--no-critic`), wizard `S_CRITIC_PICK` 빌드+제출, CLI bypass.
79
+ 9. [`CHANGES.md`](../../../CHANGES.md) — 사용자 영향 항목.
80
+
81
+ ## 5. Enforcement — 선언과 강제의 구분
82
+
83
+ - **machine-강제:** `convergence.critic` 블록 형태 + render resolve(`--critic`/`--no-critic`/무지정) → 단위 테스트. wizard `S_CRITIC_PICK` 의 picker 옵션·CLI bypass → wizard 테스트.
84
+ - **prompt-only(강제 불가):** critic 이 실제로 의미 있는 gap 을 찾는지, 1회 적대 reverify 가 환각을 거르는지는 lead/워커(LLM) 프롬프트 지시일 뿐 런타임 강제 아님 — skill/profile 선언으로 유도.
85
+
86
+ ## 6. 비용·리스크
87
+
88
+ - **비용:** opt-in(기본 off). 켜면 critic dispatch 1 + reverify 1라운드(analyser 수만큼). 기본 off 라 미선택 run 은 비용 0.
89
+ - **리스크 — 환각 gap:** critic 이 가짜 gap 을 낼 수 있음. 완화: 1회 적대 reverify 가 증거 없는 gap 을 강등. 살아남은 gap 만 finding 으로.
90
+ - **리스크 — 중복 finding:** critic 이 기존 finding 을 재서술. 완화: 프롬프트가 "이미 있는 finding 반복 금지" 명시 + reverify 단계의 semantic grouping 이 중복을 흡수.
91
+ - **리스크 — reverify 투표자 부족:** critic gap 은 critic 자신을 뺀 Phase 4 analyser 가 검증. 기본 로스터가 ≥2 analyser 라 최소 1명은 항상 투표 가능. analyser 가 1명뿐인 비정상 구성이면 critic gap 은 검증 불가로 표면화만(병합 안 함)하고 그 사실을 기록.
92
+
93
+ ## 7. 수용 기준
94
+
95
+ 1. wizard 에 `S_CRITIC_PICK` select box 가 추가되고(추천 + "직접 입력"), `--critic`/`--no-critic` 미지정 대화형 run 에서만 표시된다. 플래그 지정 시 건너뛴다.
96
+ 2. manifest `convergence.critic` 가 wizard 선택/플래그에서 정확히 resolve 된다(enabled/provider/model). 기본 off.
97
+ 3. convergence skill 이 critic pass(시점·프롬프트·1회 적대 reverify·병합)를 정의한다.
98
+ 4. 세 적용 phase 프로필이 coverage critic opt-in 을 선언한다.
99
+ 5. `python3 -m pytest tests/` + `bash validators/validate-workflow.sh` 통과.
@@ -0,0 +1,90 @@
1
+ # Acceptance devil's-advocate critic — 설계 (sub-project B2)
2
+
3
+ - 작성일: 2026-06-05
4
+ - 범위: sub-project B1 의 critic dispatch primitive(기존 worker 재사용 + provider/model 선택 + opt-in)를 `final-verification` 으로 확장한다. final-verification 에서 critic 은 **악마의 변호인** 모드로 동작 — "받아들이면 안 되는 이유/놓친 acceptance blocker 를 캐라" — 그리고 후보 blocker 는 **confirm-or-downgrade** 로 검증(확인→Acceptance Blocker, 미확인→Residual Risk, 절대 drop 안 함)되어 verdict 에 반영된다. 선택 UX·`--critic`/`S_CRITIC_PICK`·`convergence.critic` 블록은 B1 것을 그대로 재사용하며, 적용 phase 에 `final-verification` 을 추가한다.
5
+ - 비범위
6
+ - 새 dispatch 메커니즘·새 설치형 agent·새 selection 플래그 없음 — B1 primitive 전부 재사용.
7
+ - convergence 의 finding/plan-body classification·gate enum·verdict↔blocker validator 변경 없음 — 확인된 critic blocker 는 *기존* Acceptance Blockers 경로로 들어가고 기존 verdict 규칙(`accepted` ⇒ blocker 0)이 그대로 작동한다.
8
+ - B1 의 coverage critic 동작(3 finding-phase, 적대적-drop 검증) 변경 없음 — B2 는 final-verification 전용 *모드*를 추가할 뿐.
9
+ - 관계: B1 [`2026-06-04-coverage-critic-design.md`](2026-06-04-coverage-critic-design.md) 가 만든 critic primitive·`convergence.critic` 블록·`S_CRITIC_PICK` 선택을 재사용한다. final-verification 프로필의 Acceptance Blockers/Residual Risk/Verdict Token 구조([`prompts/profiles/final-verification.md`](../../../prompts/profiles/final-verification.md))에 출력을 연동한다.
10
+
11
+ ## 1. 동기
12
+
13
+ final-verification 은 수용 판정 직전의 마지막 게이트다. 거짓 합의가 여기서 새면 결함이 그대로 릴리스된다. 그런데 적대적 검증(sub-project 0)을 final-verification 에 그대로 적용하면 역효과다 — finding 이 *결함/blocker* 이므로 finding 을 적대적으로 반박하면 "재현 못 한 진짜 결함" 이 강등·누락된다(결함 민감도 하락). 그래서 final-verification 의 critic 은 finding 을 반박하는 게 아니라, **verdict 에 대한 악마의 변호인** — "이 작업을 받아들이면 안 되는 이유를 적극적으로 찾는" 추가 패스여야 한다. 이는 커버리지를 늘리되(놓친 blocker 발굴) 결함 민감도를 *높이는* 방향이다.
14
+
15
+ ## 2. 핵심 설계
16
+
17
+ ### 2.1 critic primitive 재사용 + 적용 phase 확장
18
+
19
+ B1 의 critic dispatch(선택된 provider 의 기존 subagent + provider model, opt-in)를 그대로 쓴다. 바뀌는 것은 적용 phase 집합뿐:
20
+
21
+ - [`scripts/okstra_ctl/render.py`](../../../scripts/okstra_ctl/render.py) `_build_convergence_block` 의 `critic_phases` 에 `final-verification` 을 추가 → `convergence.critic.enabled` 가 final-verification 에서도 true 가능.
22
+ - [`scripts/okstra_ctl/wizard.py`](../../../scripts/okstra_ctl/wizard.py) `S_CRITIC_PICK` 의 `applies` phase tuple + summary/confirmation 의 phase 조건에 `final-verification` 추가.
23
+ - 선택 UX(`--critic <provider>` / okstra-run select box), `convergence.critic {enabled,provider,modelExecutionValue}` 블록, 모델 해석은 **불변**(B1 그대로).
24
+
25
+ ### 2.2 critic 행동은 phase 별로 분기 (B2 의 신규 부분)
26
+
27
+ convergence skill 에서 critic 행동을 phase 로 분기한다:
28
+ - `requirements-discovery` / `error-analysis` / `implementation-planning` → **coverage critic**(B1: "뭐가 빠졌나", 적대적-drop 검증). 불변.
29
+ - `final-verification` → **acceptance devil's-advocate critic**(신규).
30
+
31
+ **악마의 변호인 프롬프트(final-verification):**
32
+ ```
33
+ You are the acceptance devil's advocate for <task-key>. The delivered work is about
34
+ to be judged for acceptance. Your ONLY job is to find reasons it should NOT be
35
+ accepted — surface candidate acceptance BLOCKERS the verifiers may have missed:
36
+ - requirements / acceptance points with no covering evidence,
37
+ - DB / IO / SQL changes lacking real-execution evidence,
38
+ - regressions or broken error paths,
39
+ - scope/contract violations.
40
+ For each, emit a candidate blocker with a one-line statement, evidence (file:line /
41
+ log / test output), and a severity (critical / major / minor). Do NOT restate an
42
+ existing Acceptance Blocker. If you find none, say so explicitly.
43
+ ```
44
+
45
+ **검증 = confirm-or-downgrade (B1 의 적대적-drop 과 다름, BLOCKING):**
46
+ 각 후보 blocker 를 Phase 4 analyser 들(critic 제외)이 검증한다.
47
+ - **확인**(재현/증거 인용 성공) → `## 4 Acceptance Blockers` 행으로 승격(severity 유지, follow-up phase 포함).
48
+ - **미확인**(재현 불가 또는 증거 약함) → **Residual Risk 로 강등(절대 drop 하지 않음)** — 추적 대상으로 남기고 trigger 를 기록.
49
+ - 적대적 finding classifier 의 "불확실하면 기각" 규칙을 여기 적용하는 것은 금지(진짜 결함을 억누름).
50
+
51
+ **출력·verdict 연동:**
52
+ - 확인된 후보가 Acceptance Blockers 에 들어가면, `accepted` 는 blocker 0 을 요구하므로([`final-verification.md:32`](../../../prompts/profiles/final-verification.md)) verdict 가 자동으로 `conditional-accept` / `blocked` 로 밀린다. 이것이 악마의 변호인의 목적이다.
53
+ - 미확인 후보는 Residual Risk 로 — verdict 를 막지는 않으나 추적된다.
54
+ - 기존 verdict↔blocker 일관성 validator(`validators/validate-run.py` `_validate_final_verification_consistency`)가 그대로 강제한다. 새 enum/validator 없음.
55
+
56
+ ### 2.3 critic 결과의 상태 기록
57
+
58
+ - critic 후보 blocker 는 `runs/final-verification/worker-results/<provider>-critic-final-verification-<seq>.md` 에 기록.
59
+ - convergence 상태 아티팩트의 `config.critic` 요약(B1 정의)에 `mode: "acceptance-devils-advocate"`, `candidatesProposed`, `confirmedBlockers`, `downgradedToResidual` 를 기록(optional v1.2 필드, reader 는 누락을 null 로). enum 변경 없음.
60
+
61
+ ## 3. 변경 파일
62
+
63
+ 1. [`scripts/okstra_ctl/render.py`](../../../scripts/okstra_ctl/render.py) — `critic_phases` 에 `final-verification` 추가.
64
+ 2. [`tests/test_render_critic_block.py`](../../../tests/test_render_critic_block.py) — `final-verification` 을 비적용→적용 파라미터로 이동(`enabled=True` 검증).
65
+ 3. [`scripts/okstra_ctl/wizard.py`](../../../scripts/okstra_ctl/wizard.py) — `S_CRITIC_PICK.applies` + summary/confirmation phase 조건에 `final-verification` 추가.
66
+ 4. [`tests/test_wizard_critic_pick.py`](../../../tests/test_wizard_critic_pick.py) — `final-verification` 을 "skipped" → "applies" 로 플립.
67
+ 5. [`skills/okstra-convergence/SKILL.md`](../../../skills/okstra-convergence/SKILL.md) — "Coverage critic pass" 절에 phase 분기 + "Acceptance devil's-advocate critic (final-verification)" 하위 모드(프롬프트·confirm-or-downgrade·blocker/residual-risk 출력·상태 요약) 추가.
68
+ 6. [`prompts/profiles/final-verification.md`](../../../prompts/profiles/final-verification.md) — 악마의 변호인 critic opt-in 선언 + 출력이 Acceptance Blockers/Residual Risk 로 들어가고 verdict 에 미치는 영향.
69
+ 7. [`agents/SKILL.md`](../../../agents/SKILL.md) — critic pass(Phase 5.6)가 final-verification 에도 적용됨을 반영(phase 행/PROGRESS 주석).
70
+ 8. (선택) [`prompts/wizard/prompts.ko.json`](../../../prompts/wizard/prompts.ko.json) — `critic_pick` label 을 phase-중립적으로 일반화(예: "추가 critic 패스(놓친 finding/blocker 발굴) — opt-in").
71
+ 9. [`CHANGES.md`](../../../CHANGES.md) — 사용자 영향 항목.
72
+
73
+ ## 4. Enforcement — 선언과 강제의 구분
74
+
75
+ - **machine-강제:** render `critic_phases` 에 final-verification 포함 + wizard `applies` 확장 → 단위/wizard 테스트. 확인된 critic blocker 가 Acceptance Blockers 로 들어갔을 때의 verdict 일관성(`accepted` ⇒ blocker 0)은 *기존* `_validate_final_verification_consistency` 가 그대로 강제.
76
+ - **prompt-only(강제 불가):** 악마의 변호인이 실제로 의미 있는 후보를 찾는지, confirm-or-downgrade 가 정확히 분류하는지는 lead/워커(LLM) 프롬프트 지시 — skill/profile 선언으로 유도.
77
+
78
+ ## 5. 비용·리스크
79
+
80
+ - **비용:** opt-in(기본 off, B1 과 동일). 켜면 critic dispatch 1 + 후보 검증 1라운드(analyser 수만큼). 미선택 final-verification run 비용 0.
81
+ - **리스크 — 후보 폭증:** critic 이 약한 후보를 다수 낼 수 있음. 완화: confirm-or-downgrade 가 미확인을 Residual Risk 로 강등하므로 verdict 를 막는 것은 *확인된* blocker 뿐. severity·증거 필수.
82
+ - **리스크 — 거짓 통과(억압) 방지가 목적인데 confirm-or-downgrade 가 미확인을 강등:** 미확인을 drop 하지 않고 Residual Risk 로 남기므로 추적은 보존. "확인" 기준은 재현/증거 인용이며, 재현이 불확실한 고-severity 후보는 Residual Risk 의 escalation trigger 로 기록해 사용자가 판단할 수 있게 한다.
83
+
84
+ ## 6. 수용 기준
85
+
86
+ 1. `final-verification` 의 manifest `convergence.critic` 가 `--critic`/wizard 선택에서 resolve 되어 `enabled=true` 가능(B1 의 3 phase + final-verification = 4 적용 phase).
87
+ 2. okstra-run `S_CRITIC_PICK` 이 final-verification 에서도 표시된다.
88
+ 3. convergence skill 이 final-verification 의 악마의 변호인 모드(프롬프트·confirm-or-downgrade·blocker/residual-risk 출력)를 정의하고, B1 coverage 모드와 명확히 구분한다.
89
+ 4. final-verification 프로필이 critic opt-in 과 verdict 영향을 선언한다.
90
+ 5. `python3 -m pytest tests/` + `bash validators/validate-workflow.sh` 통과.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "okstra",
3
- "version": "0.47.0",
3
+ "version": "0.48.0",
4
4
  "description": "Multi-agent cross-verification orchestrator runtime + Claude Code skills.",
5
5
  "license": "MIT",
6
6
  "author": "devonshin",
@@ -1,5 +1,5 @@
1
1
  {
2
- "package": "0.47.0",
3
- "builtAt": "2026-06-04T12:46:31.759Z",
2
+ "package": "0.48.0",
3
+ "builtAt": "2026-06-04T17:18:36.379Z",
4
4
  "repoRoot": "/home/runner/work/okstra/okstra"
5
5
  }
@@ -42,6 +42,7 @@ This SKILL.md is the operating contract and phase index. Detailed procedures liv
42
42
  | 4. Execution | Spawn analysis workers (Teams preferred) | `okstra-team-contract` |
43
43
  | 5. Fallback | Sequential/background dispatch when Teams unavailable | `okstra-team-contract` |
44
44
  | 5.5 Convergence | Cross-verify findings across workers | `okstra-convergence` |
45
+ | 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" |
45
46
  | 6. Synthesis | Dispatch Report writer worker, review draft. **For `implementation-planning`: then run the Phase 6 plan-body verification sub-step (see Phase 6 section below).** | `okstra-report-writer` + `okstra-convergence` (sub-step) |
46
47
  | 7. Persist | Run token-usage collector, update manifests, then disband the worker team (shutdown teammates + `TeamDelete`, after collection) | `okstra-report-writer` + `_common-contract.md` "Run-end team teardown" |
47
48
 
@@ -92,6 +93,7 @@ Required checkpoints:
92
93
  - `PROGRESS: phase-4-dispatch worker=<role> model=<model>` — once per worker, immediately before the `Agent` / wrapper call.
93
94
  - `PROGRESS: phase-5-collect worker=<role> status=<terminal-status>` — once per worker, immediately after the result file is verified.
94
95
  - `PROGRESS: phase-5.5-convergence round=<N> queue=<count>` — at the start of each convergence round (Phase 5.5).
96
+ - `PROGRESS: phase-5.6-critic provider=<provider> gaps=<n>` — when the coverage critic pass runs (Phase 5.6, opt-in). Omitted when `convergence.critic.enabled == false`.
95
97
  - `PROGRESS: phase-6-synthesis dispatching report-writer-worker` — at the start of Phase 6.
96
98
  - `PROGRESS: phase-7-persist updating manifests` — at the start of Phase 7.
97
99
  - `PROGRESS: phase-7-teardown disbanding team` — after token-usage collection, immediately before shutting down worker teammates + `TeamDelete` (Teams mode only; see `_common-contract.md` "Run-end team teardown"). Skipped in the no-`team_name` fallback.
@@ -251,7 +253,7 @@ Convergence is enabled by default. Configure via task-manifest.json:
251
253
  - `convergence.enabled`: true/false (default: true)
252
254
  - `convergence.maxRounds`: 1–3 — **phase-aware default**: `1` for `requirements-discovery`, `2` for all other task types
253
255
  - `convergence.verificationMode`: `"lightweight"` | `"full-reanalysis"` (default: `"lightweight"`; the adversarial phases below force `"full-reanalysis"`)
254
- - `convergence.adversarial`: true/false — **phase-aware default**: `true` for `requirements-discovery` / `error-analysis`, `false` otherwise. When `true`, Phase 5.5 runs in adversarial mode (verifiers refute findings; burden of proof on the claim). See [okstra-convergence](./skills/okstra-convergence/SKILL.md) "Adversarial Verification Mode".
256
+ - `convergence.adversarial`: true/false — **phase-aware default**: `true` for `requirements-discovery` / `error-analysis` / `implementation-planning`, `false` otherwise. When `true`, Phase 5.5 runs in adversarial mode (verifiers refute findings; burden of proof on the claim). See [okstra-convergence](./skills/okstra-convergence/SKILL.md) "Adversarial Verification Mode".
255
257
 
256
258
  When `task-manifest.json` does not set `convergence.maxRounds`, lead MUST resolve the effective value via the phase-aware default above before entering Phase 5.5, and record the resolved value in the convergence state artifact at `config.effectiveMaxRounds`.
257
259
 
@@ -14,7 +14,7 @@ profile document.
14
14
  - Worker interaction model (shared — read before inferring behaviour from the roster):
15
15
  - the per-profile `Required workers:` block is a **roster**, not a behaviour contract. Each role's interaction mode changes across operating phases of the same run.
16
16
  - **Phase 4 / 5 (independent analysis)**: analyser workers (`claude`, `codex`, `gemini` when opted in) produce findings independently and have no access to one another's outputs. `report-writer` does not analyse.
17
- - **Phase 5.5 (convergence — peer review by workers)**: the lead replays each analyser's findings to the *other* analysers and collects `AGREE` / `DISAGREE` / `SUPPLEMENT` verdicts across up to `effectiveMaxRounds` rounds. Workers act as peer reviewers of each other's findings in this phase; the lead mediates but does not vote. See `skills/okstra-convergence/SKILL.md` for the round protocol, queue invariants, and final classification (`full-consensus` / `partial-consensus` / `contested` / `worker-unique`). For `requirements-discovery` and `error-analysis` this phase runs in **adversarial mode** (`convergence.adversarial=true`): verifiers try to refute each finding against its cited evidence and the burden of proof sits on the claim — see that skill's §"Adversarial Verification Mode".
17
+ - **Phase 5.5 (convergence — peer review by workers)**: the lead replays each analyser's findings to the *other* analysers and collects `AGREE` / `DISAGREE` / `SUPPLEMENT` verdicts across up to `effectiveMaxRounds` rounds. Workers act as peer reviewers of each other's findings in this phase; the lead mediates but does not vote. See `skills/okstra-convergence/SKILL.md` for the round protocol, queue invariants, and final classification (`full-consensus` / `partial-consensus` / `contested` / `worker-unique`). For `requirements-discovery`, `error-analysis`, and `implementation-planning` this phase runs in **adversarial mode** (`convergence.adversarial=true`): verifiers try to refute each finding against its cited evidence and the burden of proof sits on the claim — see that skill's §"Adversarial Verification Mode".
18
18
  - Do NOT conclude "no peer review happens" from the roster alone — every profile that lists ≥2 analyser workers runs convergence by default (`convergence.enabled=true` in `task-manifest.json`).
19
19
  - Tooling — read-only MCP availability (shared):
20
20
  - MCP is not implicit okstra context. Query an MCP server only when the task brief explicitly lists it as source material for this run. Any MCP-derived finding MUST cite server, table, and the SELECT used. MCP MUST NEVER be used as a write path — schema/data mutations go through repository migration files reviewed by humans.
@@ -32,6 +32,7 @@
32
32
  - **Evidence note required inside `Statement`**: every clarification row includes `Evidence checked: <path:line>` or `Evidence checked: none — <reporter-only reason>` in the `Statement` cell. `none` is allowed ONLY when the row's nature is "only the reporter can answer this" (reporter-side data, business priority, environment they observed). A row with `none` that *could* have been answered by code or logs is a defect.
33
33
  - Cross-verification mode:
34
34
  - Phase 5.5 convergence runs in **adversarial mode** for this phase (`convergence.adversarial=true`). Verifiers actively try to refute each root-cause / reproduction claim by directly re-inspecting the cited code, logs, or config; the burden of proof sits on the claim. See `skills/okstra-convergence/SKILL.md` §"Adversarial Verification Mode". A single evidence-backed refutation prevents a finding from reaching consensus.
35
+ - **Coverage critic (opt-in)**: when `convergence.critic.enabled=true` (chosen via the okstra-run picker or `--critic`), a reused-worker critic pass runs after convergence to surface missed findings; its gaps are merged only after a 1-round adversarial reverify. See `skills/okstra-convergence/SKILL.md` "Coverage critic pass".
35
36
  - Non-goals:
36
37
  - implementation details unless they are necessary to validate the cause
37
38
  - **source code edits, builds, migrations, or deployments** — this run produces evidence and cause analysis only; the fix belongs to a later `implementation-planning` run followed by an `implementation` run