okstra 0.54.0 → 0.56.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.
Files changed (39) hide show
  1. package/bin/okstra +24 -7
  2. package/docs/project-structure-overview.md +0 -1
  3. package/docs/superpowers/plans/2026-05-25-okstra-project-root-rename.md +0 -1
  4. package/docs/superpowers/plans/2026-06-07-stage-conformance-qa-phase2.md +275 -0
  5. package/docs/superpowers/plans/2026-06-07-stage-conformance-qa-phase3.md +282 -0
  6. package/docs/superpowers/plans/2026-06-07-stage-conformance-qa-phase4a.md +147 -0
  7. package/docs/superpowers/plans/2026-06-07-stage-conformance-qa-phase4b.md +262 -0
  8. package/docs/superpowers/plans/2026-06-07-stage-conformance-qa-phase4c.md +184 -0
  9. package/docs/superpowers/plans/2026-06-07-stage-conformance-qa-phase4d.md +88 -0
  10. package/docs/superpowers/plans/2026-06-07-stage-conformance-qa-phase4e.md +250 -0
  11. package/docs/superpowers/plans/2026-06-07-stage-conformance-qa.md +409 -0
  12. package/docs/superpowers/specs/2026-06-07-stage-conformance-qa-design.md +169 -0
  13. package/package.json +1 -1
  14. package/runtime/BUILD.json +2 -2
  15. package/runtime/agents/workers/report-writer-worker.md +1 -1
  16. package/runtime/bin/lib/okstra/cli.sh +5 -1
  17. package/runtime/bin/lib/okstra/usage.sh +5 -0
  18. package/runtime/bin/okstra-inject-report-index.py +66 -0
  19. package/runtime/bin/okstra.sh +1 -0
  20. package/runtime/prompts/profiles/_implementation-verifier.md +23 -2
  21. package/runtime/prompts/profiles/final-verification.md +1 -0
  22. package/runtime/prompts/profiles/implementation-planning.md +4 -0
  23. package/runtime/prompts/profiles/improvement-discovery.md +1 -0
  24. package/runtime/python/okstra_ctl/clarification_items.py +10 -1
  25. package/runtime/python/okstra_ctl/conformance.py +270 -0
  26. package/runtime/python/okstra_ctl/paths.py +2 -0
  27. package/runtime/python/okstra_ctl/render_final_report.py +221 -2
  28. package/runtime/python/okstra_ctl/report_views.py +23 -4
  29. package/runtime/python/okstra_ctl/run.py +29 -0
  30. package/runtime/skills/okstra-run/SKILL.md +12 -0
  31. package/runtime/skills/okstra-setup/SKILL.md +35 -0
  32. package/runtime/templates/reports/i18n/en.json +6 -0
  33. package/runtime/templates/reports/i18n/ko.json +6 -0
  34. package/runtime/validators/lib/fixtures.sh +9 -0
  35. package/runtime/validators/validate-implementation-plan-stages.py +28 -3
  36. package/runtime/validators/validate-run.py +136 -1
  37. package/runtime/validators/validate_improvement_report.py +5 -1
  38. package/src/okstra-dirs.mjs +1 -1
  39. package/src/migrate.mjs +0 -146
@@ -0,0 +1,409 @@
1
+ # Stage Conformance QA (Tier 3) Implementation Plan
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:** implementation/final-verification 의 각 stage 가 상위 요구사항에 부합하는지 실DB·엔드포인트·외부 API 수준에서 검증하는 Tier 3 conformance 게이트를 추가하고, 미검증을 silent pass 가 아닌 BLOCKING 으로 만들되 명시적 우회(선언형 면제 / 사용자 확인형 waiver)를 감사한다.
6
+
7
+ **Architecture:** 모든 산출물이 의존하는 결정론적 코어(매니페스트 검증 + `QA-RESULT` 파서)를 먼저 만들고(Phase 1), 그 위에 강제(Phase 2) → 생성/실행 wiring(Phase 3) → 우회 UX/env(Phase 4)를 얹는다. 설계 SSOT: [`docs/superpowers/specs/2026-06-07-stage-conformance-qa-design.md`](../specs/2026-06-07-stage-conformance-qa-design.md).
8
+
9
+ **Tech Stack:** Python 3 (stdlib + pytest), markdown prompt profiles, Node CLI (`bin/`, `scripts/lib/okstra/`), project.json.
10
+
11
+ ---
12
+
13
+ ## 단계 분해 (Phase Roadmap)
14
+
15
+ 이 기능은 분리 가능한 4개 슬라이스다. **본 문서는 Phase 1 을 bite-sized TDD 로 완전히 기술**하고, Phase 2~4 는 각자 독립 계획으로 확장한다(Phase 1 이 데이터 계약을 락인하므로 그 뒤가 안정적). 각 phase 는 그 자체로 테스트 가능하다.
16
+
17
+ | Phase | 슬라이스 | 핵심 산출물 | 의존 |
18
+ |---|---|---|---|
19
+ | 1 | 계약 & 검증 코어 | `scripts/okstra_ctl/conformance.py` + 단위테스트 | 없음 |
20
+ | 2 | 강제(load-bearing) | `validators/validate-run.py` Tier3 BLOCKING + `run.py` 진입 게이트 | 1 |
21
+ | 3 | 생성·실행 wiring | `implementation-planning.md`/`_implementation-verifier.md`/`final-verification.md` prompt | 1,2 |
22
+ | 4 | 우회 UX & env | `--qa-waiver` CLI + wizard + `project.json.qaEnv` | 1,2 |
23
+
24
+ Phase 2~4 요약(각자 별도 계획으로 작성):
25
+
26
+ - **Phase 2** — `validators/validate-run.py` 에 conformance 게이트 추가: implementation/final-verification diff 가 `requires`(db/io/http/external) 표면을 건드리는데 매니페스트 entry 가 없거나 결과가 FAIL/MISSING 이고 면제·waiver 도 없으면 BLOCKING. 선언형 면제가 실제 surface 와 불일치하면 BLOCKING. `run.py` 의 implementation 진입 게이트(현 [`run.py:1110`](../../../scripts/okstra_ctl/run.py:1110) qaCommands 패턴 모방)에서 `validate_conformance_manifest` 호출. surface 추정은 implementation report 의 Diff Summary(파일 경로/확장자/SQL 키워드) 기반 휴리스틱 + 명시 `requires` 우선.
27
+ - **Phase 3** — `prompts/profiles/implementation-planning.md` 에 stage 별 `### 5.5.x Stage Conformance Tests` 산출 + `.okstra/.../qa/` 스크립트·매니페스트 기록 지시(`Acceptance:` 의 실행형). `_implementation-verifier.md` 의 two-tier lookup 에 **Tier 3** 추가(스크립트 실행 → `parse_qa_result` 결과를 Read-only command log 에 기록). `final-verification.md` 는 전 stage 합집합 실행.
28
+ - **Phase 4** — `scripts/okstra.sh`/`scripts/lib/okstra/cli.sh` 에 `--qa-waiver <stageKey>:"<reason>"` 패스스루(→ `run.py` 가 매니페스트 entry 의 `waiver` 채움), `okstra-run` wizard 에 사용자 확인 picker, `project.json.qaEnv`(replicaDbDsn/appBaseUrl/envFile) 문서화(resolver 는 비-identity 필드를 이미 보존하므로 코드 변경 불필요 — [`resolver.py:120`](../../../scripts/okstra_project/resolver.py:120) 확인).
29
+
30
+ ---
31
+
32
+ ## Phase 1 — 계약 & 검증 코어
33
+
34
+ 새 모듈 [`scripts/okstra_ctl/qa_commands.py`](../../../scripts/okstra_ctl/qa_commands.py) 와 동형의 sibling `conformance.py` 를 만든다. 런타임 검증/파싱 전용(순수 함수, 부수효과 없음).
35
+
36
+ ### Task 1: 매니페스트 구조 검증 (`validate_conformance_manifest`)
37
+
38
+ **Files:**
39
+ - Create: `scripts/okstra_ctl/conformance.py`
40
+ - Test: `tests/test_okstra_ctl_conformance.py`
41
+
42
+ - [ ] **Step 1: 실패 테스트 작성**
43
+
44
+ ```python
45
+ # tests/test_okstra_ctl_conformance.py
46
+ from __future__ import annotations
47
+
48
+ import sys
49
+ from pathlib import Path
50
+
51
+ _REPO_ROOT = Path(__file__).resolve().parent.parent
52
+ sys.path.insert(0, str(_REPO_ROOT / "scripts"))
53
+
54
+ from okstra_ctl.conformance import ( # noqa: E402
55
+ CAPABILITY_WHITELIST,
56
+ validate_conformance_manifest,
57
+ )
58
+
59
+
60
+ def _entry(**over):
61
+ base = {
62
+ "stageKey": "dev-9184-stage-3",
63
+ "script": "qa/stage-3.ts",
64
+ "runCommand": "npx ts-node .okstra/qa/stage-3.ts",
65
+ "requirementIds": ["R-001"],
66
+ "requires": ["db", "http"],
67
+ "passContract": "exit 0 = PASS",
68
+ "exemption": None,
69
+ "waiver": None,
70
+ }
71
+ base.update(over)
72
+ return base
73
+
74
+
75
+ def test_valid_manifest_has_no_errors():
76
+ manifest = {"entries": [_entry()]}
77
+ assert validate_conformance_manifest(manifest) == []
78
+
79
+
80
+ def test_absent_manifest_is_legal():
81
+ assert validate_conformance_manifest(None) == []
82
+
83
+
84
+ def test_non_object_manifest_rejected():
85
+ assert validate_conformance_manifest([]) != []
86
+
87
+
88
+ def test_entries_must_be_array():
89
+ assert any("entries" in e for e in validate_conformance_manifest({"entries": {}}))
90
+
91
+
92
+ def test_missing_required_string_fields_reported():
93
+ errs = validate_conformance_manifest({"entries": [_entry(stageKey="")]})
94
+ assert any("stageKey" in e for e in errs)
95
+
96
+
97
+ def test_requirement_ids_must_be_non_empty_list():
98
+ errs = validate_conformance_manifest({"entries": [_entry(requirementIds=[])]})
99
+ assert any("requirementIds" in e for e in errs)
100
+
101
+
102
+ def test_unknown_capability_rejected():
103
+ errs = validate_conformance_manifest({"entries": [_entry(requires=["db", "gpu"])]})
104
+ assert any("gpu" in e for e in errs)
105
+
106
+
107
+ def test_capability_whitelist_value():
108
+ assert CAPABILITY_WHITELIST == ("db", "io", "http", "external")
109
+ ```
110
+
111
+ - [ ] **Step 2: 실패 확인**
112
+
113
+ Run: `python3 -m pytest tests/test_okstra_ctl_conformance.py -q`
114
+ Expected: FAIL — `ModuleNotFoundError: No module named 'okstra_ctl.conformance'`
115
+
116
+ - [ ] **Step 3: 최소 구현**
117
+
118
+ ```python
119
+ # scripts/okstra_ctl/conformance.py
120
+ """Stage conformance(Tier 3) 매니페스트 검증 + `QA-RESULT` 파서.
121
+
122
+ implementation/final-verification 의 verifier 는 stage 별 conformance 스크립트를
123
+ 실행해 상위 요구사항 부합을 검증한다. 본 모듈은 그 검증/파싱의 결정론적 코어다.
124
+
125
+ 1. `conformance-manifest.json` 구조 검증 (`validate_conformance_manifest`).
126
+ 2. 스크립트 stdout 의 `QA-RESULT` 마커 파싱 (`parse_qa_result`).
127
+
128
+ 스크립트 실행/게이트 강제는 verifier prompt 와 validators/validate-run.py 가 담당한다.
129
+ """
130
+ from __future__ import annotations
131
+
132
+ import re
133
+ from dataclasses import dataclass
134
+
135
+ # diff 가 건드린 표면과 대조할 capability 태그 화이트리스트.
136
+ CAPABILITY_WHITELIST: tuple[str, ...] = ("db", "io", "http", "external")
137
+
138
+
139
+ class ConformanceError(ValueError):
140
+ """conformance 매니페스트가 계약을 어긴 경우."""
141
+
142
+
143
+ def _check_nonempty_str(value: object, path: str, errors: list[str]) -> bool:
144
+ if not isinstance(value, str) or not value.strip():
145
+ errors.append(f"{path} must be a non-empty string")
146
+ return False
147
+ return True
148
+
149
+
150
+ def _check_capabilities(value: object, path: str, errors: list[str]) -> None:
151
+ if not isinstance(value, list):
152
+ errors.append(f"{path} must be an array")
153
+ return
154
+ for cap in value:
155
+ if cap not in CAPABILITY_WHITELIST:
156
+ errors.append(
157
+ f"{path}: unknown capability {cap!r} "
158
+ f"(allowed: {', '.join(CAPABILITY_WHITELIST)})"
159
+ )
160
+
161
+
162
+ def _check_entry(entry: object, idx: int, errors: list[str]) -> None:
163
+ path = f"entries[{idx}]"
164
+ if not isinstance(entry, dict):
165
+ errors.append(f"{path} must be an object")
166
+ return
167
+ _check_nonempty_str(entry.get("stageKey"), f"{path}.stageKey", errors)
168
+ _check_nonempty_str(entry.get("script"), f"{path}.script", errors)
169
+ _check_nonempty_str(entry.get("runCommand"), f"{path}.runCommand", errors)
170
+ _check_nonempty_str(entry.get("passContract"), f"{path}.passContract", errors)
171
+ req_ids = entry.get("requirementIds")
172
+ if (
173
+ not isinstance(req_ids, list)
174
+ or not req_ids
175
+ or not all(isinstance(r, str) and r.strip() for r in req_ids)
176
+ ):
177
+ errors.append(f"{path}.requirementIds must be a non-empty array of strings")
178
+ _check_capabilities(entry.get("requires", []), f"{path}.requires", errors)
179
+
180
+
181
+ def validate_conformance_manifest(manifest: object) -> list[str]:
182
+ """conformance-manifest 전체 검증. 위반 메시지 리스트 반환(비면 안전).
183
+
184
+ 매니페스트 부재(None)는 합법 — 스크립트 없는 task 가 있을 수 있고, 게이트
185
+ 강제(diff surface 대조)는 validators/validate-run.py 가 판정한다.
186
+ """
187
+ if manifest is None:
188
+ return []
189
+ if not isinstance(manifest, dict):
190
+ return [f"conformance manifest must be an object, got {type(manifest).__name__}"]
191
+ entries = manifest.get("entries")
192
+ if not isinstance(entries, list):
193
+ return ["conformance manifest .entries must be an array"]
194
+ errors: list[str] = []
195
+ for idx, entry in enumerate(entries):
196
+ _check_entry(entry, idx, errors)
197
+ return errors
198
+ ```
199
+
200
+ - [ ] **Step 4: 통과 확인**
201
+
202
+ Run: `python3 -m pytest tests/test_okstra_ctl_conformance.py -q`
203
+ Expected: PASS (8 passed)
204
+
205
+ - [ ] **Step 5: 커밋**
206
+
207
+ ```bash
208
+ git add scripts/okstra_ctl/conformance.py tests/test_okstra_ctl_conformance.py
209
+ git commit -m "feat(okstra_ctl/conformance): conformance 매니페스트 구조 검증"
210
+ ```
211
+
212
+ ### Task 2: 면제/waiver 형태 검증 + stageKey 중복
213
+
214
+ **Files:**
215
+ - Modify: `scripts/okstra_ctl/conformance.py`
216
+ - Test: `tests/test_okstra_ctl_conformance.py`
217
+
218
+ - [ ] **Step 1: 실패 테스트 추가**
219
+
220
+ ```python
221
+ def test_exemption_requires_reason_and_declared_at():
222
+ bad = _entry(exemption={"reason": ""})
223
+ errs = validate_conformance_manifest({"entries": [bad]})
224
+ assert any("exemption.reason" in e for e in errs)
225
+ assert any("exemption.declaredAt" in e for e in errs)
226
+
227
+
228
+ def test_valid_exemption_passes():
229
+ ok = _entry(exemption={"reason": "doc-only stage", "declaredAt": "2026-06-07"})
230
+ assert validate_conformance_manifest({"entries": [ok]}) == []
231
+
232
+
233
+ def test_waiver_requires_fields_and_scope_whitelist():
234
+ bad = _entry(waiver={"acknowledgedBy": "user", "reason": "env down",
235
+ "at": "2026-06-07", "scope": ["db", "gpu"]})
236
+ errs = validate_conformance_manifest({"entries": [bad]})
237
+ assert any("waiver.scope" in e and "gpu" in e for e in errs)
238
+
239
+
240
+ def test_valid_waiver_passes():
241
+ ok = _entry(waiver={"acknowledgedBy": "user", "reason": "replica down — skip db",
242
+ "at": "2026-06-07T10:00:00Z", "scope": ["db"]})
243
+ assert validate_conformance_manifest({"entries": [ok]}) == []
244
+
245
+
246
+ def test_duplicate_stage_key_reported():
247
+ errs = validate_conformance_manifest({"entries": [_entry(), _entry()]})
248
+ assert any("duplicate" in e for e in errs)
249
+ ```
250
+
251
+ - [ ] **Step 2: 실패 확인**
252
+
253
+ Run: `python3 -m pytest tests/test_okstra_ctl_conformance.py -q`
254
+ Expected: FAIL — 면제/waiver 미검증, 중복 미검출
255
+
256
+ - [ ] **Step 3: 구현 추가**
257
+
258
+ `conformance.py` 의 `_check_entry` 끝(`_check_capabilities(... "requires" ...)` 다음 줄)에 추가:
259
+
260
+ ```python
261
+ _check_exemption(entry.get("exemption"), f"{path}.exemption", errors)
262
+ _check_waiver(entry.get("waiver"), f"{path}.waiver", errors)
263
+ ```
264
+
265
+ `_check_capabilities` 정의 다음에 두 헬퍼 추가:
266
+
267
+ ```python
268
+ def _check_exemption(value: object, path: str, errors: list[str]) -> None:
269
+ if value is None:
270
+ return
271
+ if not isinstance(value, dict):
272
+ errors.append(f"{path} must be an object or null")
273
+ return
274
+ _check_nonempty_str(value.get("reason"), f"{path}.reason", errors)
275
+ _check_nonempty_str(value.get("declaredAt"), f"{path}.declaredAt", errors)
276
+
277
+
278
+ def _check_waiver(value: object, path: str, errors: list[str]) -> None:
279
+ if value is None:
280
+ return
281
+ if not isinstance(value, dict):
282
+ errors.append(f"{path} must be an object or null")
283
+ return
284
+ _check_nonempty_str(value.get("acknowledgedBy"), f"{path}.acknowledgedBy", errors)
285
+ _check_nonempty_str(value.get("reason"), f"{path}.reason", errors)
286
+ _check_nonempty_str(value.get("at"), f"{path}.at", errors)
287
+ _check_capabilities(value.get("scope", []), f"{path}.scope", errors)
288
+ ```
289
+
290
+ `validate_conformance_manifest` 의 루프를 중복 검출로 교체:
291
+
292
+ ```python
293
+ errors: list[str] = []
294
+ seen: set[str] = set()
295
+ for idx, entry in enumerate(entries):
296
+ _check_entry(entry, idx, errors)
297
+ key = entry.get("stageKey") if isinstance(entry, dict) else None
298
+ if isinstance(key, str) and key:
299
+ if key in seen:
300
+ errors.append(f"entries[{idx}].stageKey duplicate: {key!r}")
301
+ seen.add(key)
302
+ return errors
303
+ ```
304
+
305
+ - [ ] **Step 4: 통과 확인**
306
+
307
+ Run: `python3 -m pytest tests/test_okstra_ctl_conformance.py -q`
308
+ Expected: PASS (13 passed)
309
+
310
+ - [ ] **Step 5: 커밋**
311
+
312
+ ```bash
313
+ git add scripts/okstra_ctl/conformance.py tests/test_okstra_ctl_conformance.py
314
+ git commit -m "feat(okstra_ctl/conformance): 면제/waiver 형태 + stageKey 중복 검증"
315
+ ```
316
+
317
+ ### Task 3: `QA-RESULT` stdout 파서 (`parse_qa_result`)
318
+
319
+ **Files:**
320
+ - Modify: `scripts/okstra_ctl/conformance.py`
321
+ - Test: `tests/test_okstra_ctl_conformance.py`
322
+
323
+ - [ ] **Step 1: 실패 테스트 추가**
324
+
325
+ ```python
326
+ from okstra_ctl.conformance import parse_qa_result # noqa: E402 (파일 상단 import 에 합쳐도 됨)
327
+
328
+
329
+ def test_parse_pass_with_requirements():
330
+ out = "running...\nREQ R-001: PASS: row count 3 > 0\nQA-RESULT: PASS\n"
331
+ res = parse_qa_result(out)
332
+ assert res.overall == "PASS"
333
+ assert res.requirements["R-001"]["status"] == "PASS"
334
+ assert "row count" in res.requirements["R-001"]["reason"]
335
+
336
+
337
+ def test_parse_fail():
338
+ res = parse_qa_result("REQ R-001: FAIL: parity mismatch\nQA-RESULT: FAIL\n")
339
+ assert res.overall == "FAIL"
340
+ assert res.requirements["R-001"]["status"] == "FAIL"
341
+
342
+
343
+ def test_missing_marker_is_missing():
344
+ assert parse_qa_result("no marker here").overall == "MISSING"
345
+ assert parse_qa_result("").overall == "MISSING"
346
+
347
+
348
+ def test_last_marker_wins():
349
+ assert parse_qa_result("QA-RESULT: PASS\nQA-RESULT: FAIL\n").overall == "FAIL"
350
+ ```
351
+
352
+ - [ ] **Step 2: 실패 확인**
353
+
354
+ Run: `python3 -m pytest tests/test_okstra_ctl_conformance.py -q`
355
+ Expected: FAIL — `cannot import name 'parse_qa_result'`
356
+
357
+ - [ ] **Step 3: 구현 추가**
358
+
359
+ `conformance.py` 끝에 추가:
360
+
361
+ ```python
362
+ _QA_RESULT_RE = re.compile(r"^QA-RESULT:\s*(PASS|FAIL)\s*$", re.MULTILINE)
363
+ _REQ_LINE_RE = re.compile(r"^REQ\s+(\S+):\s*(PASS|FAIL):\s*(.*)$", re.MULTILINE)
364
+
365
+
366
+ @dataclass
367
+ class QaResult:
368
+ overall: str # "PASS" | "FAIL" | "MISSING"
369
+ requirements: dict # id -> {"status": "PASS"|"FAIL", "reason": str}
370
+
371
+
372
+ def parse_qa_result(stdout: str) -> QaResult:
373
+ """스크립트 stdout 에서 `QA-RESULT` 마커 + `REQ` 줄 파싱.
374
+
375
+ 마커가 없으면 overall='MISSING' — 스크립트가 계약을 안 지킨 것이므로 게이트는
376
+ FAIL 로 취급한다. 마커가 여럿이면 마지막 것을 채택한다.
377
+ """
378
+ text = stdout or ""
379
+ markers = _QA_RESULT_RE.findall(text)
380
+ overall = markers[-1] if markers else "MISSING"
381
+ requirements: dict = {}
382
+ for rid, status, reason in _REQ_LINE_RE.findall(text):
383
+ requirements[rid] = {"status": status, "reason": reason.strip()}
384
+ return QaResult(overall=overall, requirements=requirements)
385
+ ```
386
+
387
+ - [ ] **Step 4: 통과 확인**
388
+
389
+ Run: `python3 -m pytest tests/test_okstra_ctl_conformance.py -q`
390
+ Expected: PASS (17 passed)
391
+
392
+ - [ ] **Step 5: 전체 스위트 회귀 확인 + 커밋**
393
+
394
+ ```bash
395
+ python3 -m pytest tests/ -q
396
+ git add scripts/okstra_ctl/conformance.py tests/test_okstra_ctl_conformance.py
397
+ git commit -m "feat(okstra_ctl/conformance): QA-RESULT stdout 파서"
398
+ ```
399
+
400
+ ---
401
+
402
+ ## Phase 1 Self-Review 체크
403
+
404
+ - [ ] spec §3(데이터 모델) 의 모든 entry 필드(stageKey/script/runCommand/requirementIds/requires/passContract/exemption/waiver)가 `validate_conformance_manifest` 로 커버되는가?
405
+ - [ ] spec §4(스크립트 계약)의 `QA-RESULT`/`REQ` 마커가 `parse_qa_result` 로 커버되는가?
406
+ - [ ] capability 화이트리스트가 spec §3 의 `["db","io","http","external"]` 와 일치하는가?
407
+ - [ ] Phase 2 가 의존하는 공개 API(`validate_conformance_manifest`, `parse_qa_result`, `CAPABILITY_WHITELIST`, `QaResult`)가 안정적으로 노출되는가?
408
+
409
+ Phase 1 완료 후 Phase 2 계획(`docs/superpowers/plans/2026-06-07-stage-conformance-qa-phase2.md`)을 작성한다.
@@ -0,0 +1,169 @@
1
+ # Stage 요구사항-부합 검증 (Tier 3 Conformance QA) 설계
2
+
3
+ - 상태: 설계 승인됨 (구현 전)
4
+ - 날짜: 2026-06-07
5
+ - 관련 코드: [`scripts/okstra_ctl/qa_commands.py`](../../../scripts/okstra_ctl/qa_commands.py), [`prompts/profiles/_implementation-verifier.md`](../../../prompts/profiles/_implementation-verifier.md), [`prompts/profiles/implementation-planning.md`](../../../prompts/profiles/implementation-planning.md), [`prompts/profiles/final-verification.md`](../../../prompts/profiles/final-verification.md), [`scripts/okstra_ctl/run.py`](../../../scripts/okstra_ctl/run.py), [`validators/validate-run.py`](../../../validators/validate-run.py)
6
+
7
+ ## 1. 배경과 문제
8
+
9
+ implementation run 은 정적 분석 + 모킹 유닛테스트 게이트를 통과해도, **실DB·프로젝트 엔드포인트·외부 API 같은 통합 표면**은 검증하지 못한 채 "정적 분석상 OK, 미검증(실행 안 함)"으로 남는 구조적 공백이 있다.
10
+
11
+ 근거 — DEV-9184 (`fontradar-v2-api`) Stage 3:
12
+
13
+ - 1930 유닛테스트 green + tsc clean 으로 정적/모킹 게이트는 통과했으나 **종합 verdict 는 FAIL**.
14
+ - 원인 ①: DB real-execution 게이트(G-2/V-5) — diff 가 DB/SQL 을 건드리는데 `db-test` 미선언 → `정적 분석상 OK, 미검증(실행 안 함)`.
15
+ - 원인 ②: 모킹이 못 잡은 런타임 버그(Q-1, full-consensus). 실DB·실행 없이는 표면화 불가.
16
+ - FU-002 의 처방이 정확히 이 기능의 형태였다: replica DB 에서 `SELECT COUNT(*) FROM LicenseVersionMinPrices WHERE idLicenseVersion=<DRAFT> > 0`, `GET /price/list` pre/post read↔publish parity 검증.
17
+
18
+ 핵심 통찰: 이 공백은 "검증 명령 부재"가 *조용히* PASS 로 흐른 데서 비롯됐다(silent mock-green). 따라서 해법은 (a) stage 별 통합 검증 스크립트를 1급 산출물로 만들고, (b) 그 스크립트가 없거나 못 돌 때 **조용히 통과시키지 않는 것**이다.
19
+
20
+ ## 2. 목표 / 비목표
21
+
22
+ 목표:
23
+
24
+ - implementation 의 각 stage(및 final-verification)에 대해, **상위 요구사항(task brief → requirements-discovery / error-analysis / improvement-discovery → plan stage Acceptance)에 부합하는지**를 검증하는 실행 스크립트를 산출·실행·판정한다.
25
+ - 유닛/모킹이 닿지 못하는 표면(실DB·엔드포인트·외부 API)을 커버한다.
26
+ - 검증 미실행을 **silent pass 가 아니라 BLOCKING** 으로 만든다. 단, **명시적으로 확인된 우회 경로**를 감사 흔적과 함께 제공한다.
27
+
28
+ 비목표:
29
+
30
+ - 유닛테스트 자체의 대체(executor 의 RED→GREEN TDD 루프는 그대로 유지). 이 기능은 그 위의 **통합/부합 게이트**다.
31
+ - 사용자 repo 의 정식 테스트 스위트를 okstra 가 소유/관리하는 것(Approach C 기각). conformance 스크립트는 okstra artifact 로 `.okstra/` 하위에 둔다.
32
+ - 정적·프로젝트 전역 `qaCommands` 베이스라인을 per-stage 데이터로 오염시키는 것(Approach B 기각).
33
+
34
+ ## 3. 데이터 모델 (무엇을 · 어디에)
35
+
36
+ artifact-home 규칙에 따라 모든 산출물은 `.okstra/` 하위에 둔다.
37
+
38
+ - 스크립트: `.okstra/tasks/<task-key>/runs/implementation/qa/stage-<N>.<ext>` (stage 당 1개 이상).
39
+ - conformance 매니페스트: `.okstra/tasks/<task-key>/runs/implementation/qa/conformance-manifest.json`
40
+
41
+ ```json
42
+ {
43
+ "entries": [
44
+ {
45
+ "stageKey": "<task-id>-stage-<N>",
46
+ "script": "qa/stage-3.ts",
47
+ "runCommand": "npx ts-node .okstra/.../qa/stage-3.ts",
48
+ "requirementIds": ["R-001", "GAP-1"],
49
+ "requires": ["db", "http"],
50
+ "passContract": "exit 0 = PASS, non-zero = FAIL",
51
+ "exemption": null,
52
+ "waiver": null
53
+ }
54
+ ]
55
+ }
56
+ ```
57
+
58
+ - 요구사항 추적성: 각 entry 의 `requirementIds` 는 plan §5.5.8 Requirement Coverage 의 `R-NNN`, 그리고 brief / requirements-discovery / error-analysis / improvement-discovery 에서 이월된 ID 를 가리킨다. "이 스크립트가 어떤 요구사항의 부합을 증명하는가"를 명시한다.
59
+ - `requires`: capability 태그 화이트리스트 `["db", "io", "http", "external"]`. diff 가 건드린 표면과 대조해 게이트 강도를 결정한다.
60
+
61
+ ## 4. 표준 스크립트 계약 (`main → pass/fail`)
62
+
63
+ - 진입점 `main()` 실행 → **exit code 0 = PASS / 비0 = FAIL**.
64
+ - stdout 마지막 줄에 파싱용 마커: `QA-RESULT: PASS|FAIL`, 그리고 요구사항별 줄 `REQ <id>: PASS|FAIL: <근거>`.
65
+ - 언어는 프로젝트 따라감(`qaCommands` 의 `language` 와 정합). 예: DEV-9184 는 TS 스크립트가 `GET /price/list` 호출 + replica DB 에 `SELECT COUNT(*) … WHERE idLicenseVersion=<DRAFT>` 실행 후 read↔publish parity 를 판정.
66
+ - 스크립트는 기본적으로 black-box(HTTP/SQL 등 외부에서 관측)로 작성한다. 프로젝트 코드 import 가 필요한 경우는 예외로, 프로젝트 모듈 해석 경로에 맞춘 배치를 허용한다(엣지).
67
+
68
+ ## 5. 생성 시점 · 주체
69
+
70
+ - implementation-planning 이 stage 별로:
71
+ 1. plan 에 `### 5.5.x Stage Conformance Tests` 블록을 산출한다. 블록은 스크립트 본문 + 헤더(`stageKey` / `requirementIds` / `requires`)를 담는다.
72
+ 2. 그 스크립트를 `.okstra/.../qa/` 에 파일로 기록하고 `conformance-manifest.json` 을 생성한다.
73
+ - 근거: `Acceptance:` 라인이 이미 plan 의 1급 요소이므로, conformance 스크립트는 "Acceptance 를 실행 가능하게 만든 것"으로서 plan deliverable 의 자연스러운 확장이다(planning=설계 원칙과 정합 — 테스트 코드는 구현이 아니라 수용 기준의 실행형).
74
+
75
+ ## 6. 실행 · 판정 (verifier Tier 3 + final-verification)
76
+
77
+ - verifier 의 명령 lookup 에 **Tier 3** 추가:
78
+ - Tier 1 = plan validation (pre/mid/post),
79
+ - Tier 2 = `project.json.qaCommands`,
80
+ - **Tier 3 = conformance 스크립트** (해당 stage 의 매니페스트 entry).
81
+ - verifier 는 stage 의 스크립트를 worktree / replica env 에서 실행하고, exact runCommand + exit code + `QA-RESULT` 파싱 결과를 Read-only command log 와 증거에 기록한다.
82
+ - final-verification: 전 stage 의 스크립트 합집합을 통합 state 에 대해 실행한다.
83
+ - **핵심 불변식 — 미실행 = BLOCKING**: diff 가 `requires`(db/io/http/external) 표면을 건드리는데 스크립트 부재 / env 부재 / 미실행이면, DEV-9184 처럼 passive note 가 아니라 **verdict FAIL** 로 격상한다(silent mock-green 금지).
84
+
85
+ ## 7. 명시적 우회 경로
86
+
87
+ "건너뛰려면 둘 중 하나가 반드시 있어야 하고, 둘 다 리포트에 감사 흔적으로 남는다." silent skip 은 불가.
88
+
89
+ ### 7.1 Planning 선언형 면제
90
+
91
+ - stage 에 `Conformance exemption: <사유>` 라인을 선언(기존 `TDD exemption:` 패턴과 동형).
92
+ - 용도: 테스트가 정말 불필요한 stage(doc / config / pure-rename, 또는 db/io/external 을 안 건드리고 유닛으로 충분).
93
+ - 안전장치: diff 가 실제로 db/io/external 을 건드리면 선언형 면제는 **거부(BLOCKING)** — 면제 사유와 실제 surface 가 일치할 때만 허용. (면제로 실DB 변경을 숨기는 것 방지)
94
+ - 기록: 매니페스트 entry 의 `exemption: { reason, declaredAt }`.
95
+
96
+ ### 7.2 Run-time 사용자 확인형 우회 (waiver)
97
+
98
+ - conformance 가 필요한데(surface 건드림) 사용자가 의도적으로 건너뛰는 경우.
99
+ - **사용자 명시 확인 필수**(lead / worker 자체 면제 불가):
100
+ - interactive(`okstra-run`): 명시적 확인 프롬프트(추천 picker, 마지막 옵션 "직접 입력").
101
+ - headless(`okstra.sh`): `--qa-waiver <stageKey>:"<사유>"` 명시 인자.
102
+ - 기록: 매니페스트 / 리포트에 `waiver: { acknowledgedBy, reason, scope: ["db" | "http" | …], at }`. `reason` 은 **사용자 지시 원문 그대로**(artifact-home 규칙의 "사용자 지시 verbatim 인용"과 동형).
103
+ - verdict 반영: waiver 가 적용된 run 은 clean PASS 가 아니라 **`conditional-accept` (conformance 미검증 — 사용자 확인)** 로 기록 → final-verification 에서 그 조건이 그대로 노출된다. `conditional-accept` 는 기존 final-verdict verdict token 을 재사용.
104
+ - scope 단위: stage 전체 또는 capability 별(예: `db` 만 waive, `http` 는 그대로 실행) — allowlist 방식으로 좁게.
105
+
106
+ ## 8. env · 안전 모델
107
+
108
+ - `project.json` 에 정적 `qaEnv` 블록 추가(예: `replicaDbDsn`, `appBaseUrl`, `envFile` 참조). per-stage 스크립트는 번들에, env 포인터는 `project.json` 에(정적 / 민감정보 분리). `qaEnv` 는 resolver 의 user-managed 보존 필드로 둔다(identity/timestamp 필드만 runtime-owned).
109
+ - 안전: 기본 **replica / test env 전용**. mutation 을 일으키는 스크립트는 capability 태그로 명시한다. 기존 `qaCommands` 의 mutation deny-list 정신을 conformance 스크립트 실행 환경 가드에도 적용한다.
110
+ - env / secret 부재 시 → §6 의 BLOCKING(절대 silent pass 아님). 단 §7.2 사용자 확인형 waiver 로만 우회 가능.
111
+
112
+ ## 9. 검증 · 강제 (이 기능 자체)
113
+
114
+ - 매니페스트 스키마 validator: entry 필수 필드(`stageKey`, `script`, `runCommand`, `requirementIds`, `requires`, `passContract`), `requires` 화이트리스트, `exemption`/`waiver` 형태.
115
+ - verifier Tier 3 lookup 단위테스트(스크립트 있음/없음/면제/waiver 분기).
116
+ - 강제 케이스: "diff 가 `requires` 표면을 건드림 + 스크립트/결과/면제/waiver 모두 없음 → BLOCKING FAIL".
117
+ - 선언형 면제 거부 케이스: "면제 선언했지만 diff 가 db/io/external 을 건드림 → BLOCKING".
118
+ - `QA-RESULT` 파싱 테스트(PASS/FAIL/누락).
119
+ - 우회 감사 흔적 테스트: waiver 시 리포트에 사용자 원문 인용 + `conditional-accept` 반영.
120
+
121
+ ## 10. 리스크 / 열린 질문
122
+
123
+ - replica DB / 실행 env 준비는 프로젝트별 운영 부담. `qaEnv` 미설정 시 게이트가 항상 BLOCKING → 도입 초기에는 §7 우회로 점진 적용.
124
+ - planning 이 실행 컨텍스트 없이 작성한 스크립트가 틀릴 수 있음 → 스크립트는 구현 전 RED(실패), 구현 후 GREEN(통과)으로 동작해야 하며, verifier 가 실제 실행으로 검증한다(정적 작성 ≠ 검증).
125
+ - 동시 병렬 stage run(stage worktree isolation)에서 conformance 스크립트가 공유 replica DB 를 건드릴 때의 격리/오염 — 초기엔 직렬 실행 가정, 추후 stage 별 env 분리 고려.
126
+
127
+ ## 11. 롤아웃
128
+
129
+ - pre-1.0 이므로 호환 shim 없음. `qaCommands` 부재 프로젝트는 기존처럼 Tier 2 미구성으로 동작하되, Tier 3 는 매니페스트가 있을 때만 활성.
130
+ - 사용자 영향(`CHANGES.md`)에 `사용자 영향:` 라인으로 기록.
131
+ - 산출물은 seed/template/runtime sync 경로를 통해 end user 에 전파(`npm run build`).
132
+
133
+ ## 12. Phase 4 addendum (2026-06-07) — 위치·생성·실행·강제 wiring 확정
134
+
135
+ Phase 1~3(계약·판정·validate-run 게이트) 구현 중 드러난 설계 갭을 brainstorm 으로 해소한 결정. Phase 4 구현의 SSOT.
136
+
137
+ ### 12.1 산출물 위치 — task-level (확정)
138
+ - conformance 매니페스트/스크립트/`result-<stageKey>.json` 은 **`<task_root>/qa/`** (task-level) 에 둔다. planning→implementation→final-verification 3개 task-type 이 공유해야 하므로 run-level(`runs/<task-type>/qa/`)로는 final-verification 이 implementation 산출물을 못 본다.
139
+ - `scripts/okstra_ctl/paths.py` 에 `task_qa()` / `TASK_QA_PATH` 토큰을 추가해 단일 출처로 삼는다(run.py/prompt/validator 의 경로 재유도 drift 방지).
140
+ - **Phase 3 게이트 경로 수정(잠복 결함)**: `validators/validate-run.py` 의 `_validate_conformance` 는 현재 `report_path.parent.parent/qa`(run-level)를 읽는다 — task-level 결정에 따라 `<task_root>/qa` 로 바꾼다. report_path 는 `task_root/runs/<task-type>/reports/final-report.md` 이므로 `run_dir = report_path.parent.parent` (=`runs/<task-type>`), `task_root = run_dir.parent.parent`, `qa_dir = task_root / "qa"`. (주의: `report_path.parent.parent.parent` 는 `runs/` 라 잘못됨 — 한 단계 더 올라가야 task_root.) Phase 3 테스트 fixture 도 task-level 로 갱신한다.
141
+ - 병렬 stage run 안전성: stage 별 `result-<stageKey>.json` 파일명이 달라 동시 쓰기 충돌 없음. 매니페스트는 planning 이 1회 작성.
142
+
143
+ ### 12.2 planning 생성 + 선언 강제
144
+ - `prompts/profiles/implementation-planning.md`: stage 마다 conformance entry(스크립트 + `<task_root>/qa/conformance-manifest.json` 갱신) **또는** `Conformance exemption: <사유>` 라인을 산출한다(기존 `TDD exemption:` 패턴과 동형).
145
+ - `validators/validate-implementation-plan-stages.py` 에 새 검사(S11): 모든 stage 가 conformance entry 또는 exemption 을 가질 것 — 미선언 시 planning boundary 에서 BLOCKING.
146
+
147
+ ### 12.3 verifier 실행 (Tier 3)
148
+ - `prompts/profiles/_implementation-verifier.md`: command lookup 을 Tier1(plan validation) → Tier2(`qaCommands`) → **Tier3(conformance)** 로 확장. 각 entry 의 `runCommand` 를 worktree cwd + `qaEnv` 로 실행 → `parse_qa_result` → `<task_root>/qa/result-<stageKey>.json` 작성(포맷 `{stageKey, overall, ranAt, requirements}` — 코드가 이미 고정).
149
+
150
+ ### 12.4 diff-surface 교차검증 (DEV-9184 자동 차단)
151
+ - `scripts/okstra_ctl/conformance.py` 에 순수 함수 `detect_surfaces(file_paths) -> set[str]` 추가. 기본 패턴(`*.sql`·`migrations/`·`*repository*`·`*.entity.*` → `db`; `*controller*`·`*.routes.*` → `http`; 등), `project.json.qaEnv.surfacePatterns` 로 override.
152
+ - `validators/validate-run.py`: **한 run = 한 stage** 이므로 그 run 의 Diff Summary 파일 목록에서 surface 를 감지하고, 해당 stage 의 선언(`requires` + exemption)이 그 surface 를 커버하지 못하면 BLOCKING. (순수 함수 → TDD 가능; validate-run 은 report 의 §5.7.3 Diff Summary 파일 경로를 입력으로 사용)
153
+
154
+ ### 12.5 env 모델
155
+ - `project.json.qaEnv`: `replicaDbDsn` / `appBaseUrl` / `envFile`(+ 선택 `surfacePatterns`). `scripts/okstra_project/resolver.py` 는 비-identity 필드를 이미 자동 보존하므로 코드 변경 불필요 — 스키마/`skills/okstra-setup/SKILL.md`/verifier prompt 만 문서화.
156
+ - 안전: replica/test 전용, mutation 스크립트는 capability 태그로 명시, 기존 `qaCommands` mutation deny-list 정신 유지.
157
+
158
+ ### 12.6 final-verification
159
+ - `prompts/profiles/final-verification.md`: task-level `<task_root>/qa/` 에서 전 stage 스크립트 합집합을 통합 state 에 대해 실행, command lookup 에 Tier3 추가.
160
+
161
+ ### 12.7 우회 UX (spec §7.2 구체화)
162
+ - `scripts/okstra.sh` / `scripts/lib/okstra/cli.sh` 에 `--qa-waiver <stageKey>:"<reason>"` 패스스루 → `scripts/okstra_ctl/run.py` 가 `<task_root>/qa/conformance-manifest.json` 의 해당 entry `waiver` 를 채움(`acknowledgedBy`/`reason`(원문)/`scope`/`at`). `okstra-run` wizard 는 사용자 확인 picker(추천 + "직접 입력").
163
+
164
+ ### 12.8 Phase 4 sub-plan 분해(구현 단위)
165
+ 1. **4a** — 위치 SSOT: `paths.py` TASK_QA 토큰 + Phase 3 게이트 경로/테스트 task-level 전환.
166
+ 2. **4b** — `detect_surfaces` (순수) + validate-run diff-surface 교차검증.
167
+ 3. **4c** — planning 생성 + S11 선언 강제(prompt + validator).
168
+ 4. **4d** — verifier Tier3 실행(prompt) + final-verification Tier3(prompt).
169
+ 5. **4e** — env(`qaEnv` 스키마/setup/문서) + 우회 UX(`--qa-waiver` CLI + wizard).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "okstra",
3
- "version": "0.54.0",
3
+ "version": "0.56.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.54.0",
3
- "builtAt": "2026-06-06T15:10:15.217Z",
2
+ "package": "0.56.0",
3
+ "builtAt": "2026-06-07T06:39:30.395Z",
4
4
  "repoRoot": "/home/runner/work/okstra/okstra"
5
5
  }
@@ -101,7 +101,7 @@ Rules (the schema enforces most of these — they are listed here so you know *w
101
101
  - Cite file paths and line numbers in every `evidence.primary[].source` / `consensus[].evidence` cell.
102
102
  - Preserve every analysis worker's ticket tagging — every row's `ticketId` field carries the ticket key or the task-fallback. For single-ticket runs, set `ticketCoverage` to `{"singleTicket": "<ticket>"}`. For runs that do not require ticket tagging (`release-handoff`, `final-verification`), set `ticketCoverage` to `{"omit": true}`.
103
103
  - For `implementation-planning`, populate `implementationPlanning.requirementCoverage` with one row per concrete requirement from the brief / packet, using IDs `R-001`, `R-002`, ... in source order. `coveredBy` MUST name the specific Option Candidate plus Stage/Step that satisfies the requirement. Use `status: "covered"` only when the report's plan actually covers it; otherwise use `gap` or `blocked C-NNN` and ensure the corresponding `Clarification Items` row blocks approval. Do not collapse this into `ticketCoverage`; ticket coverage is not requirement coverage.
104
- - When the `Task Type` is `improvement-discovery`, populate `## 5.9 Improvement Candidates` with the 10-column schema enforced by `validators/validate-improvement-report.py`. Source the row IDs (`I-NNN`), lens whitelist, and Source workers patterns from `scripts/okstra_ctl/improvement_lenses.py` — do NOT introduce new lens names or worker prefixes.
104
+ - When the `Task Type` is `improvement-discovery`, populate `## 5.9 Improvement Candidates` with the 10-column schema enforced by `validators/validate-improvement-report.py`. Source the row IDs (`I-NNN`), lens whitelist, and Source workers patterns from `scripts/okstra_ctl/improvement_lenses.py` — do NOT introduce new lens names or worker prefixes. `improvement-discovery` is NOT in the data.json schema enum, so author its markdown directly (not via `okstra-render-final-report.py`). Immediately after writing the markdown, run (`Bash`): `python3 scripts/okstra-inject-report-index.py <markdown path> --report-language <en|ko>`. That adds the top-of-report Index plus `I-NNN` / `C-NNN` scroll anchors; the run validator fails the report when the Index anchor is absent.
105
105
 
106
106
  Write the data.json with your `Write` tool against the absolute `Result Path`. Then invoke the renderer (`Bash`): `python3 scripts/okstra-render-final-report.py <data.json path>`. Confirm both files exist and respond with a short status line: `data.json written to <abs path>; markdown rendered to <abs path>. Sections populated: <count>.`
107
107
 
@@ -99,6 +99,10 @@ while [[ $# -gt 0 ]]; do
99
99
  STAGE="$(require_option_value --stage "${2-}")"
100
100
  shift 2
101
101
  ;;
102
+ --qa-waiver)
103
+ QA_WAIVER="$(require_option_value --qa-waiver "${2-}")"
104
+ shift 2
105
+ ;;
102
106
  --task-type)
103
107
  TASK_TYPE="$(require_option_value --task-type "${2-}")"
104
108
  shift 2
@@ -189,7 +193,7 @@ while [[ $# -gt 0 ]]; do
189
193
  printf ' hint: did you mean --task-id?\n' >&2
190
194
  ;;
191
195
  esac
192
- printf ' valid options: --render-only --resume-clarification --yes --workers --lead-model --claude-model --codex-model --gemini-model --report-writer-model --related-tasks --task-type --project-id --project-root --task-group --task-id --task-brief --directive --clarification-response --approved-plan --approve --implementation-option --stage --no-plan-verification -h|--help\n' >&2
196
+ printf ' valid options: --render-only --resume-clarification --yes --workers --lead-model --claude-model --codex-model --gemini-model --report-writer-model --related-tasks --task-type --project-id --project-root --task-group --task-id --task-brief --directive --clarification-response --approved-plan --approve --implementation-option --stage --qa-waiver --no-plan-verification -h|--help\n' >&2
193
197
  usage
194
198
  exit 1
195
199
  ;;
@@ -52,6 +52,11 @@ optional arguments:
52
52
  --task-type=implementation. The runtime fills the approved-plan frontmatter
53
53
  \`implementation-option:\` line with <name>. When omitted, the implementation run
54
54
  falls back to the plan's \`Recommended Option\`.
55
+ --qa-waiver <stageKey>:<reason>
56
+ Stage conformance gate 우회(사용자 확인형). Only meaningful with
57
+ --task-type=implementation. prepare-time 에 task-level conformance 매니페스트의
58
+ 해당 stageKey entry.waiver 를 채운다(reason 은 사용자 지시 원문 그대로 기록).
59
+ lead/worker 가 스스로 면제할 수 없으며 사용자가 명시할 때만 사용한다.
55
60
  --no-plan-verification
56
61
  Disable the Phase 6 plan-body verification round that runs after the report-writer
57
62
  authors the implementation-planning draft. Default: enabled. Only meaningful with