okstra 0.55.0 → 0.56.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/okstra +24 -7
- package/docs/kr/architecture.md +2 -2
- package/docs/project-structure-overview.md +0 -1
- package/docs/superpowers/plans/2026-05-25-okstra-project-root-rename.md +0 -1
- package/docs/superpowers/plans/2026-06-07-stage-conformance-qa-phase2.md +275 -0
- package/docs/superpowers/plans/2026-06-07-stage-conformance-qa-phase3.md +282 -0
- package/docs/superpowers/plans/2026-06-07-stage-conformance-qa-phase4a.md +147 -0
- package/docs/superpowers/plans/2026-06-07-stage-conformance-qa-phase4b.md +262 -0
- package/docs/superpowers/plans/2026-06-07-stage-conformance-qa-phase4c.md +184 -0
- package/docs/superpowers/plans/2026-06-07-stage-conformance-qa-phase4d.md +88 -0
- package/docs/superpowers/plans/2026-06-07-stage-conformance-qa-phase4e.md +250 -0
- package/docs/superpowers/plans/2026-06-07-stage-conformance-qa.md +409 -0
- package/docs/superpowers/specs/2026-06-07-stage-conformance-qa-design.md +169 -0
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/bin/lib/okstra/cli.sh +5 -1
- package/runtime/bin/lib/okstra/usage.sh +5 -0
- package/runtime/bin/okstra.sh +1 -0
- package/runtime/prompts/profiles/_common-contract.md +4 -4
- package/runtime/prompts/profiles/_implementation-deliverable.md +4 -4
- package/runtime/prompts/profiles/_implementation-executor.md +1 -4
- package/runtime/prompts/profiles/_implementation-verifier.md +23 -2
- package/runtime/prompts/profiles/final-verification.md +2 -1
- package/runtime/prompts/profiles/implementation-planning.md +9 -5
- package/runtime/prompts/profiles/implementation.md +6 -6
- package/runtime/prompts/profiles/improvement-discovery.md +1 -0
- package/runtime/prompts/profiles/release-handoff.md +4 -4
- package/runtime/python/okstra_ctl/conformance.py +270 -0
- package/runtime/python/okstra_ctl/paths.py +2 -0
- package/runtime/python/okstra_ctl/run.py +29 -0
- package/runtime/schemas/final-report-v1.0.schema.json +127 -10
- package/runtime/skills/okstra-coding-preflight/SKILL.md +8 -0
- package/runtime/skills/okstra-coding-preflight/clean-code.md +6 -0
- package/runtime/skills/okstra-run/SKILL.md +12 -0
- package/runtime/skills/okstra-run/templates/pr-body.template.md +12 -12
- package/runtime/skills/okstra-setup/SKILL.md +35 -0
- package/runtime/templates/reports/final-report.template.md +63 -19
- package/runtime/templates/reports/i18n/en.json +1 -1
- package/runtime/templates/reports/i18n/ko.json +1 -1
- package/runtime/templates/reports/implementation-input.template.md +1 -1
- package/runtime/templates/reports/implementation-planning-input.template.md +3 -3
- package/runtime/validators/validate-implementation-plan-stages.py +28 -3
- package/runtime/validators/validate-run.py +98 -0
- package/src/okstra-dirs.mjs +1 -1
- package/src/migrate.mjs +0 -146
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# Stage Conformance QA — Phase 4a (위치 SSOT + 게이트 task-level 전환) 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:** conformance 산출물의 단일 출처를 task-level `<task_root>/qa/` 로 확정한다 — `paths.py` 에 `TASK_QA_PATH` 토큰을 추가하고, Phase 3 게이트(`_validate_conformance`)가 run-level 대신 task-level `qa/` 를 읽도록 고친다(Phase 3 의 final-verification 잠복 결함 해소).
|
|
6
|
+
|
|
7
|
+
**Architecture:** 순수 경로/검증 변경. SSOT: [`docs/superpowers/specs/2026-06-07-stage-conformance-qa-design.md`](../specs/2026-06-07-stage-conformance-qa-design.md) §12.1.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** Python 3 (stdlib + pytest).
|
|
10
|
+
|
|
11
|
+
**전제:** Phase 1~3 완료. `validators/validate-run.py` 에 `_validate_conformance` 존재, `tests/test_validate_run_conformance.py` 존재.
|
|
12
|
+
|
|
13
|
+
**경로 사실(확정):** 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/` 이므로 잘못 — 한 단계 더 올라가야 함.)
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Task 1: `paths.py` 에 TASK_QA_PATH 토큰 추가
|
|
18
|
+
|
|
19
|
+
**Files:**
|
|
20
|
+
- Modify: `scripts/okstra_ctl/paths.py`
|
|
21
|
+
- Test: `tests/test_task_path_ssot.py`
|
|
22
|
+
|
|
23
|
+
- [ ] **Step 1: 실패 테스트 추가**
|
|
24
|
+
|
|
25
|
+
`tests/test_task_path_ssot.py` 끝에 추가:
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
def test_task_qa_path_under_task_root():
|
|
29
|
+
root = Path("/tmp/proj")
|
|
30
|
+
ctx = compute_run_paths(
|
|
31
|
+
workspace_root=Path("/ws"),
|
|
32
|
+
project_root=root,
|
|
33
|
+
project_id="p",
|
|
34
|
+
task_group="My Group",
|
|
35
|
+
task_id="Task ID 01",
|
|
36
|
+
task_type="implementation",
|
|
37
|
+
run_seq_override=1,
|
|
38
|
+
)
|
|
39
|
+
assert ctx["TASK_QA_PATH"] == str(Path(ctx["TASK_ROOT"]) / "qa")
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
- [ ] **Step 2: 실패 확인**
|
|
43
|
+
|
|
44
|
+
Run: `python3 -m pytest tests/test_task_path_ssot.py -q`
|
|
45
|
+
Expected: FAIL — `KeyError: 'TASK_QA_PATH'`
|
|
46
|
+
|
|
47
|
+
- [ ] **Step 3: 구현**
|
|
48
|
+
|
|
49
|
+
`scripts/okstra_ctl/paths.py` 의 `analysis_packet = instruction_set / "analysis-packet.md"` (line 119 부근) 다음 줄에 추가:
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
task_qa = task_root / "qa"
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
같은 함수의 `abs_paths` 딕셔너리에서 `"ANALYSIS_PACKET_PATH": str(analysis_packet),` 다음 줄에 추가:
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
"TASK_QA_PATH": str(task_qa),
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
- [ ] **Step 4: 통과 확인**
|
|
62
|
+
|
|
63
|
+
Run: `python3 -m pytest tests/test_task_path_ssot.py -q`
|
|
64
|
+
Expected: PASS
|
|
65
|
+
|
|
66
|
+
- [ ] **Step 5: 커밋**
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
git add scripts/okstra_ctl/paths.py tests/test_task_path_ssot.py
|
|
70
|
+
git commit -m "feat(okstra_ctl/paths): conformance task-level qa 경로 토큰(TASK_QA_PATH)"
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Task 2: 게이트를 task-level `qa/` 로 전환
|
|
74
|
+
|
|
75
|
+
**Files:**
|
|
76
|
+
- Modify: `validators/validate-run.py`
|
|
77
|
+
- Test: `tests/test_validate_run_conformance.py`
|
|
78
|
+
|
|
79
|
+
- [ ] **Step 1: 테스트 fixture 를 task-level 로 갱신(먼저 실패 유도)**
|
|
80
|
+
|
|
81
|
+
`tests/test_validate_run_conformance.py` 의 `_make_run` 에서 qa 디렉터리를 task_root(=`tmp_path`) 아래로 옮긴다. 현재:
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
run_dir = tmp_path / "runs" / "implementation"
|
|
85
|
+
(run_dir / "reports").mkdir(parents=True)
|
|
86
|
+
qa = run_dir / "qa"
|
|
87
|
+
qa.mkdir()
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
로 바꾼다:
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
run_dir = tmp_path / "runs" / "implementation"
|
|
94
|
+
(run_dir / "reports").mkdir(parents=True)
|
|
95
|
+
qa = tmp_path / "qa" # task-level: <task_root>/qa (task_root = tmp_path)
|
|
96
|
+
qa.mkdir()
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
(`test_no_manifest_is_inert` 는 qa 를 만들지 않으므로 그대로 inert — 변경 불필요.)
|
|
100
|
+
|
|
101
|
+
- [ ] **Step 2: 실패 확인**
|
|
102
|
+
|
|
103
|
+
Run: `python3 -m pytest tests/test_validate_run_conformance.py -q`
|
|
104
|
+
Expected: FAIL — 게이트가 아직 run-level(`run_dir/qa`)을 읽어 매니페스트를 못 찾고 inert 처리 → `test_missing_result_is_blocking`/`test_failing_result_is_blocking`/`test_malformed_manifest_reported` 가 기대 실패를 못 내고 FAIL.
|
|
105
|
+
|
|
106
|
+
- [ ] **Step 3: 구현 — 게이트 경로 수정**
|
|
107
|
+
|
|
108
|
+
`validators/validate-run.py` 의 `_validate_conformance` 에서:
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
qa_dir = report_path.parent.parent / "qa"
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
를 다음으로 바꾼다:
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
# conformance 산출물은 task-level(<task_root>/qa)에 있어 planning/
|
|
118
|
+
# implementation/final-verification 가 공유한다. report_path 는
|
|
119
|
+
# task_root/runs/<task-type>/reports/final-report.md 이므로 task_root 는
|
|
120
|
+
# run_dir(=report_path.parent.parent)에서 두 단계 위.
|
|
121
|
+
run_dir = report_path.parent.parent
|
|
122
|
+
qa_dir = run_dir.parent.parent / "qa"
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
- [ ] **Step 4: 통과 확인 + 전체 회귀 + workflow validator**
|
|
126
|
+
|
|
127
|
+
Run: `python3 -m pytest tests/test_validate_run_conformance.py -q` → Expected: PASS (7 passed)
|
|
128
|
+
Run: `python3 -m pytest tests/ -q` → Expected: 전부 통과
|
|
129
|
+
Run: `bash validators/validate-workflow.sh` → Expected: PASS (fixture 에 `<task_root>/qa` 없음 → inert)
|
|
130
|
+
|
|
131
|
+
- [ ] **Step 5: 커밋**
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
git add validators/validate-run.py tests/test_validate_run_conformance.py
|
|
135
|
+
git commit -m "fix(validators): conformance 게이트를 task-level qa 로 전환(final-verification 공유)"
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Phase 4a Self-Review 체크
|
|
141
|
+
|
|
142
|
+
- [ ] `TASK_QA_PATH == <task_root>/qa` 인가?
|
|
143
|
+
- [ ] 게이트가 task_root/qa 를 읽어 implementation·final-verification 가 같은 위치를 보는가?
|
|
144
|
+
- [ ] inert(매니페스트 부재) 동작 유지 — 기존 run/ workflow validator 무영향?
|
|
145
|
+
- [ ] 전체 pytest + workflow validator 통과?
|
|
146
|
+
|
|
147
|
+
Phase 4a 완료 후 Phase 4b(`detect_surfaces` + diff-surface 교차검증) 계획을 작성한다.
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
# Stage Conformance QA — Phase 4b (diff-surface 교차검증) 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 리포트의 변경 파일에서 db/io/http surface 를 감지하고, 그 surface 를 매니페스트가 선언(`requires`)하지 않았으면 BLOCKING 한다 — DEV-9184 의 "diff 가 db 를 건드렸는데 미선언/오면제 → silent pass" 를 자동 차단.
|
|
6
|
+
|
|
7
|
+
**Architecture:** 두 순수 함수(`detect_surfaces`, `manifest_required_surfaces`)를 `conformance.py` 에 추가(TDD) + validate-run 의 `_validate_conformance` 에 리포트 §5.7.3 Diff Summary 파싱 + 교차검증 추가. **매니페스트 레벨 커버리지**(detected − union(requires))로 판정하므로 리포트→stageKey 매핑이 불필요하다(한 run=한 stage, 매니페스트는 task 전체). exemption 된 stage 의 누락 surface 도 union 에 안 들어가 자동으로 BLOCKING(spec §7.1 false-exemption). SSOT: [`docs/superpowers/specs/2026-06-07-stage-conformance-qa-design.md`](../specs/2026-06-07-stage-conformance-qa-design.md) §12.4.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** Python 3 (stdlib `fnmatch`/`re` + pytest).
|
|
10
|
+
|
|
11
|
+
**전제:** Phase 1~4a 완료. `conformance.py` 에 매니페스트/판정 함수, `validate-run.py` 에 `_validate_conformance`(task-level qa) 존재.
|
|
12
|
+
|
|
13
|
+
**범위 한정:** diff-surface 교차검증은 **implementation 리포트(§5.7.3 Diff Summary 표 존재)** 에만 적용. final-verification 의 git-stat 기반 감지는 후속(이번 범위 아님). final-verification 의 결과-기반 게이트(Phase 3)는 그대로 작동.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Task 1: surface 감지 순수 함수
|
|
18
|
+
|
|
19
|
+
**Files:**
|
|
20
|
+
- Modify: `scripts/okstra_ctl/conformance.py`
|
|
21
|
+
- Test: `tests/test_okstra_ctl_conformance.py`
|
|
22
|
+
|
|
23
|
+
- [ ] **Step 1: 실패 테스트 추가**
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
from okstra_ctl.conformance import ( # noqa: E402 (기존 import 에 합쳐도 됨)
|
|
27
|
+
detect_surfaces,
|
|
28
|
+
manifest_required_surfaces,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def test_detect_surfaces_db_from_repository_and_sql():
|
|
33
|
+
assert detect_surfaces(["src/foo/license.repository.ts"]) == {"db"}
|
|
34
|
+
assert detect_surfaces(["db/migrations/001_init.sql"]) == {"db"}
|
|
35
|
+
assert detect_surfaces(["src/user/user.entity.ts"]) == {"db"}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_detect_surfaces_http_from_controller():
|
|
39
|
+
assert detect_surfaces(["src/price/price.controller.ts"]) == {"http"}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def test_detect_surfaces_none_for_plain_file():
|
|
43
|
+
assert detect_surfaces(["src/util/format.ts"]) == set()
|
|
44
|
+
assert detect_surfaces([]) == set()
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def test_detect_surfaces_multiple():
|
|
48
|
+
got = detect_surfaces(["a.repository.ts", "b.controller.ts"])
|
|
49
|
+
assert got == {"db", "http"}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def test_detect_surfaces_custom_patterns_override():
|
|
53
|
+
got = detect_surfaces(["weird/thing.xyz"], patterns={"external": ("*.xyz",)})
|
|
54
|
+
assert got == {"external"}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def test_manifest_required_surfaces_union():
|
|
58
|
+
manifest = {"entries": [
|
|
59
|
+
{"stageKey": "s1", "requires": ["db"]},
|
|
60
|
+
{"stageKey": "s2", "requires": ["http", "db"]},
|
|
61
|
+
]}
|
|
62
|
+
assert manifest_required_surfaces(manifest) == {"db", "http"}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def test_manifest_required_surfaces_empty():
|
|
66
|
+
assert manifest_required_surfaces({"entries": []}) == set()
|
|
67
|
+
assert manifest_required_surfaces(None) == set()
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
- [ ] **Step 2: 실패 확인**
|
|
71
|
+
|
|
72
|
+
Run: `python3 -m pytest tests/test_okstra_ctl_conformance.py -q`
|
|
73
|
+
Expected: FAIL — `cannot import name 'detect_surfaces'`
|
|
74
|
+
|
|
75
|
+
- [ ] **Step 3: 구현 추가**
|
|
76
|
+
|
|
77
|
+
`conformance.py` 상단 import 에 `import fnmatch` 추가(기존 `import re` 옆). 모듈 끝에 추가:
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
# 경로 → capability surface 기본 매핑. 프로젝트별 override 는 qaEnv.surfacePatterns
|
|
81
|
+
# (Phase 4e). 'external' 은 경로로 감지하기 어려워 기본 패턴 없음 — 명시 선언 의존.
|
|
82
|
+
_DEFAULT_SURFACE_PATTERNS: dict[str, tuple[str, ...]] = {
|
|
83
|
+
"db": ("*.sql", "*migration*", "*repository*", "*.entity.*", "*entities*", "*schema.prisma*"),
|
|
84
|
+
"http": ("*controller*", "*.routes.*", "*router*", "*endpoint*", "*.api.*"),
|
|
85
|
+
"io": ("*filesystem*", "*storage*", "*.fs.*"),
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def detect_surfaces(file_paths: object, patterns: object = None) -> set[str]:
|
|
90
|
+
"""변경된 파일 경로들에서 capability surface 집합을 감지(소문자 fnmatch).
|
|
91
|
+
`patterns` 미지정 시 기본 매핑 사용."""
|
|
92
|
+
table = patterns if isinstance(patterns, dict) else _DEFAULT_SURFACE_PATTERNS
|
|
93
|
+
found: set[str] = set()
|
|
94
|
+
for raw in file_paths or []:
|
|
95
|
+
if not isinstance(raw, str):
|
|
96
|
+
continue
|
|
97
|
+
path = raw.strip().lower()
|
|
98
|
+
for surface, globs in table.items():
|
|
99
|
+
if any(fnmatch.fnmatch(path, g) for g in globs):
|
|
100
|
+
found.add(surface)
|
|
101
|
+
return found
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def manifest_required_surfaces(manifest: object) -> set[str]:
|
|
105
|
+
"""매니페스트 전 entry 의 `requires` 합집합 — 선언된 surface 집합."""
|
|
106
|
+
entries = manifest.get("entries") if isinstance(manifest, dict) else None
|
|
107
|
+
if not isinstance(entries, list):
|
|
108
|
+
return set()
|
|
109
|
+
out: set[str] = set()
|
|
110
|
+
for entry in entries:
|
|
111
|
+
if isinstance(entry, dict) and isinstance(entry.get("requires"), list):
|
|
112
|
+
out.update(c for c in entry["requires"] if isinstance(c, str))
|
|
113
|
+
return out
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
- [ ] **Step 4: 통과 확인**
|
|
117
|
+
|
|
118
|
+
Run: `python3 -m pytest tests/test_okstra_ctl_conformance.py -q`
|
|
119
|
+
Expected: PASS (기존 28 + 신규 7 = 35 passed)
|
|
120
|
+
|
|
121
|
+
- [ ] **Step 5: 커밋**
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
git add scripts/okstra_ctl/conformance.py tests/test_okstra_ctl_conformance.py
|
|
125
|
+
git commit -m "feat(okstra_ctl/conformance): diff surface 감지(detect_surfaces) + 선언 surface 합집합"
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Task 2: validate-run 에 diff-surface 교차검증 wiring
|
|
129
|
+
|
|
130
|
+
**Files:**
|
|
131
|
+
- Modify: `validators/validate-run.py`
|
|
132
|
+
- Test: `tests/test_validate_run_conformance.py`
|
|
133
|
+
|
|
134
|
+
- [ ] **Step 1: 실패 테스트 추가**
|
|
135
|
+
|
|
136
|
+
`tests/test_validate_run_conformance.py` 에 헬퍼 + 테스트 추가. 리포트 본문에 §5.7.3 Diff Summary 표를 넣어 `_make_run` 을 확장한다(기존 테스트 호환 위해 신규 헬퍼 사용):
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
def _make_run_with_diff(tmp_path: Path, entries, results, diff_files):
|
|
140
|
+
run_dir = tmp_path / "runs" / "implementation"
|
|
141
|
+
(run_dir / "reports").mkdir(parents=True)
|
|
142
|
+
qa = tmp_path / "qa"
|
|
143
|
+
qa.mkdir()
|
|
144
|
+
(qa / "conformance-manifest.json").write_text(json.dumps({"entries": entries}))
|
|
145
|
+
for key, overall in results.items():
|
|
146
|
+
(qa / f"result-{key}.json").write_text(
|
|
147
|
+
json.dumps({"stageKey": key, "overall": overall, "requirements": {}})
|
|
148
|
+
)
|
|
149
|
+
rows = "\n".join(f"| `{f}` | modify | `+1/-0` | step |" for f in diff_files)
|
|
150
|
+
report = run_dir / "reports" / "final-report-implementation-001.md"
|
|
151
|
+
report.write_text(
|
|
152
|
+
"# fixture\n\n### 5.7.3 Diff Summary\n\n"
|
|
153
|
+
"| File | Action | Lines (+/-) | Plan step |\n"
|
|
154
|
+
"|------|--------|-------------|-----------|\n"
|
|
155
|
+
f"{rows}\n\n### 5.7.4 Out-of-plan Edits\n\n- none\n"
|
|
156
|
+
)
|
|
157
|
+
return report
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def test_diff_touches_undeclared_db_surface_is_blocking(tmp_path):
|
|
161
|
+
v = _load_validator()
|
|
162
|
+
# stage declares NO requires; diff touches a repository (db) → BLOCKING
|
|
163
|
+
entry = _entry("t-stage-1", requires=[])
|
|
164
|
+
report = _make_run_with_diff(tmp_path, [entry], {"t-stage-1": "PASS"},
|
|
165
|
+
["src/foo/x.repository.ts"])
|
|
166
|
+
failures: list[str] = []
|
|
167
|
+
v._validate_conformance(report, failures)
|
|
168
|
+
assert any("undeclared surface" in f and "db" in f for f in failures)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def test_declared_db_surface_no_surface_failure(tmp_path):
|
|
172
|
+
v = _load_validator()
|
|
173
|
+
entry = _entry("t-stage-1", requires=["db"])
|
|
174
|
+
report = _make_run_with_diff(tmp_path, [entry], {"t-stage-1": "PASS"},
|
|
175
|
+
["src/foo/x.repository.ts"])
|
|
176
|
+
failures: list[str] = []
|
|
177
|
+
v._validate_conformance(report, failures)
|
|
178
|
+
assert not any("undeclared surface" in f for f in failures)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def test_plain_diff_no_surface_failure(tmp_path):
|
|
182
|
+
v = _load_validator()
|
|
183
|
+
entry = _entry("t-stage-1", requires=[])
|
|
184
|
+
report = _make_run_with_diff(tmp_path, [entry], {"t-stage-1": "PASS"},
|
|
185
|
+
["src/util/format.ts"])
|
|
186
|
+
failures: list[str] = []
|
|
187
|
+
v._validate_conformance(report, failures)
|
|
188
|
+
assert failures == []
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
- [ ] **Step 2: 실패 확인**
|
|
192
|
+
|
|
193
|
+
Run: `python3 -m pytest tests/test_validate_run_conformance.py -q`
|
|
194
|
+
Expected: FAIL — surface 교차검증 미구현으로 `test_diff_touches_undeclared_db_surface_is_blocking` FAIL
|
|
195
|
+
|
|
196
|
+
- [ ] **Step 3: 구현 추가**
|
|
197
|
+
|
|
198
|
+
`validators/validate-run.py` 상단 conformance import 에 두 함수 추가:
|
|
199
|
+
|
|
200
|
+
```python
|
|
201
|
+
from okstra_ctl.conformance import ( # noqa: E402
|
|
202
|
+
detect_surfaces,
|
|
203
|
+
evaluate_conformance,
|
|
204
|
+
manifest_required_surfaces,
|
|
205
|
+
qa_result_from_dict,
|
|
206
|
+
validate_conformance_manifest,
|
|
207
|
+
)
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
`validate_report` 정의 앞(또는 `_validate_conformance` 근처)에 파서 추가:
|
|
211
|
+
|
|
212
|
+
```python
|
|
213
|
+
_DIFF_SUMMARY_RE = re.compile(r"^###\s+5\.7\.3\b.*?(?=^###\s|\Z)", re.MULTILINE | re.DOTALL)
|
|
214
|
+
_DIFF_ROW_PATH_RE = re.compile(r"^\|\s*`([^`]+)`\s*\|", re.MULTILINE)
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def _parse_diff_summary_files(content: str) -> list[str]:
|
|
218
|
+
"""implementation 리포트 §5.7.3 Diff Summary 표의 첫 셀(백틱 경로)들을 추출."""
|
|
219
|
+
section = _DIFF_SUMMARY_RE.search(content)
|
|
220
|
+
if section is None:
|
|
221
|
+
return []
|
|
222
|
+
return _DIFF_ROW_PATH_RE.findall(section.group(0))
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
`_validate_conformance` 의 결과-게이트 루프(`for verdict in evaluate_conformance(...)`) **다음**에 추가:
|
|
226
|
+
|
|
227
|
+
```python
|
|
228
|
+
changed_files = _parse_diff_summary_files(report_path.read_text(encoding="utf-8"))
|
|
229
|
+
if changed_files:
|
|
230
|
+
uncovered = detect_surfaces(changed_files) - manifest_required_surfaces(manifest)
|
|
231
|
+
if uncovered:
|
|
232
|
+
failures.append(
|
|
233
|
+
"conformance gate BLOCKING: implementation diff touches undeclared "
|
|
234
|
+
f"surface(s) {sorted(uncovered)} — no stage declares `requires` for "
|
|
235
|
+
"them. Declare a conformance entry (requires=[...]) for the touching "
|
|
236
|
+
"stage, or an explicit exemption. (silent mock-green 방지 — DEV-9184)"
|
|
237
|
+
)
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
- [ ] **Step 4: 통과 + 전체 회귀 + workflow validator**
|
|
241
|
+
|
|
242
|
+
Run: `python3 -m pytest tests/test_validate_run_conformance.py -q` → Expected: PASS (10 passed)
|
|
243
|
+
Run: `python3 -m pytest tests/ -q` → Expected: 전부 통과
|
|
244
|
+
Run: `bash validators/validate-workflow.sh` → Expected: PASS (fixture 에 §5.7.3 없음 + qa 없음 → 무영향)
|
|
245
|
+
|
|
246
|
+
- [ ] **Step 5: 커밋**
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
git add validators/validate-run.py tests/test_validate_run_conformance.py
|
|
250
|
+
git commit -m "feat(validators): diff-surface 교차검증 — 미선언 surface BLOCKING"
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## Phase 4b Self-Review 체크
|
|
256
|
+
|
|
257
|
+
- [ ] `detect_surfaces` 기본 패턴이 db/http/io 를 합리적으로 잡는가? override 동작?
|
|
258
|
+
- [ ] 미선언 surface → BLOCKING, 선언/plain → 통과가 spec §12.4 와 일치?
|
|
259
|
+
- [ ] §5.7.3 파서가 다른 표를 오인하지 않는가(섹션 스코프 한정)?
|
|
260
|
+
- [ ] 전체 pytest + workflow validator 통과(회귀 없음)?
|
|
261
|
+
|
|
262
|
+
Phase 4b 완료 후 Phase 4c(planning 생성 + S11 선언 강제) 계획을 작성한다.
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# Stage Conformance QA — Phase 4c (planning 선언 강제 S11 + 생성) 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-planning 의 모든 stage 가 conformance 검증을 선언(`Conformance tests:`)하거나 명시 면제(`Conformance exemption:`)하도록 강제한다(S11, planning boundary). + planning prompt 가 stage 별 스크립트 + `<task_root>/qa/conformance-manifest.json` 을 산출하도록 지시.
|
|
6
|
+
|
|
7
|
+
**Architecture:** `validators/validate-implementation-plan-stages.py` 에 S11 검사를 S10 과 동형으로 추가(순수 markdown 검사, TDD). + `prompts/profiles/implementation-planning.md` 에 생성 계약 추가(prescriptive). S11 은 plan markdown 만 검사하고, 매니페스트 JSON 구조는 이미 `validate_conformance_manifest`(run.py/validate-run)가 검증하므로 분리. SSOT: [`docs/superpowers/specs/2026-06-07-stage-conformance-qa-design.md`](../specs/2026-06-07-stage-conformance-qa-design.md) §12.2.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** Python 3 (pytest), markdown prompt.
|
|
10
|
+
|
|
11
|
+
**전제:** Phase 1~4b 완료.
|
|
12
|
+
|
|
13
|
+
**⚠️ Ripple 주의:** S11 은 새 필수 검사다. stage validator 에 통과해야 하는 기존 valid plan fixture(`tests/fixtures/plans/valid_one_stage.md`, `valid_three_stage_parallel.md`, 단위 테스트의 `_one_stage_plan`, 그리고 `test_run_stage_arg.py` / `test_plan_body_verification.py` / `test_okstra_ctl_wizard.py` / `test_okstra_ctl_review_fixes.py` 의 인라인 valid plan)는 stage 마다 `Conformance tests:` 라인을 추가해야 한다. 전체 스위트를 돌려 깨지는 valid-plan 테스트를 찾아 **최소 한 줄만** 보강한다(assertion/logic 변경 금지).
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Task 1: S11 검사 + 기존 valid plan fixture 보강
|
|
18
|
+
|
|
19
|
+
**Files:**
|
|
20
|
+
- Modify: `validators/validate-implementation-plan-stages.py`
|
|
21
|
+
- Modify: `tests/test_validate_implementation_plan_stages.py`
|
|
22
|
+
- Modify: `tests/fixtures/plans/valid_one_stage.md`, `tests/fixtures/plans/valid_three_stage_parallel.md` (+ 전체 스위트가 가리키는 다른 valid plan)
|
|
23
|
+
|
|
24
|
+
- [ ] **Step 1: S11 검사 구현**
|
|
25
|
+
|
|
26
|
+
`validate-implementation-plan-stages.py` 의 `TDD_EXEMPTION = re.compile(...)` (line 170 부근) 다음에 추가:
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
CONFORMANCE_TESTS = re.compile(r"^\s*Conformance tests\s*:\s*\S", re.M)
|
|
30
|
+
CONFORMANCE_EXEMPTION = re.compile(r"^\s*Conformance exemption\s*:\s*\S", re.M)
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`_check_slice_tdd` 함수 정의 다음에 새 함수 추가:
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
def _check_conformance_declaration(
|
|
37
|
+
text: str, stages: List[StageMeta]
|
|
38
|
+
) -> List[ValidationError]:
|
|
39
|
+
"""S11: 각 stage 는 conformance 검증을 선언하거나 명시적으로 면제한다.
|
|
40
|
+
|
|
41
|
+
S11 — `Conformance tests:` 라인(Tier3 검증 스크립트 선언) 또는
|
|
42
|
+
`Conformance exemption:` 라인(테스트 불필요 사유) 중 하나 필수.
|
|
43
|
+
diff 가 db/io/http surface 를 건드렸는데 아무 선언이 없는 silent-pass(DEV-9184)
|
|
44
|
+
를 planning boundary 에서 차단한다.
|
|
45
|
+
"""
|
|
46
|
+
errs: List[ValidationError] = []
|
|
47
|
+
for s in stages:
|
|
48
|
+
section = _slice_stage_section(text, s.stage_number)
|
|
49
|
+
if not (CONFORMANCE_TESTS.search(section) or CONFORMANCE_EXEMPTION.search(section)):
|
|
50
|
+
errs.append(ValidationError(
|
|
51
|
+
"S11", s.stage_number,
|
|
52
|
+
"S11: stage must declare 'Conformance tests:' (Tier3 검증 스크립트) "
|
|
53
|
+
"or 'Conformance exemption:' (사유) — stage conformance QA design §12.2",
|
|
54
|
+
))
|
|
55
|
+
return errs
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
aggregator(약 line 292, `errors.extend(_check_slice_tdd(text, stages))` 다음 줄)에 추가:
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
errors.extend(_check_conformance_declaration(text, stages))
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
docstring 헤더(line 2) `S1–S10` → `S1–S11`, 그리고 `code: str # S1..S10` (line 43 부근) → `# S1..S11` 로 갱신.
|
|
65
|
+
|
|
66
|
+
- [ ] **Step 2: 단위 테스트의 fixture 헬퍼 보강 + S11 테스트 추가**
|
|
67
|
+
|
|
68
|
+
`tests/test_validate_implementation_plan_stages.py` 의 `_one_stage_plan` 시그니처에 기본 conformance 라인을 추가한다:
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
def _one_stage_plan(slice_line, accept_line, steps,
|
|
72
|
+
conformance_line="Conformance tests: stage-1 — qa/stage-1.ts (requires=[db])"):
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
그리고 본문에서 `{accept_line}` 다음 줄에 `{conformance_line}` 을 끼운다:
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
{slice_line}
|
|
79
|
+
{accept_line}
|
|
80
|
+
{conformance_line}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
파일 끝의 S10 섹션 다음에 S11 테스트 추가:
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
# --- S11: conformance declaration / exemption ---
|
|
87
|
+
|
|
88
|
+
def test_conformance_missing_rejected():
|
|
89
|
+
r = _run_validator(_one_stage_plan(
|
|
90
|
+
"Slice value: bar 가 동작한다",
|
|
91
|
+
"Acceptance: pytest -k bar PASS",
|
|
92
|
+
_RED_GREEN,
|
|
93
|
+
conformance_line="",
|
|
94
|
+
))
|
|
95
|
+
assert "S11" in r.stderr
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def test_conformance_exemption_accepted(tmp_path):
|
|
99
|
+
r = _run_validator(_one_stage_plan(
|
|
100
|
+
"Slice value: 문서만 갱신",
|
|
101
|
+
"Acceptance: docs 빌드 통과",
|
|
102
|
+
_RED_GREEN,
|
|
103
|
+
conformance_line="Conformance exemption: doc-only stage, no db/io/http surface",
|
|
104
|
+
))
|
|
105
|
+
assert "S11" not in r.stderr
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def test_conformance_tests_declaration_accepted():
|
|
109
|
+
r = _run_validator(_one_stage_plan(
|
|
110
|
+
"Slice value: bar 가 동작한다",
|
|
111
|
+
"Acceptance: pytest -k bar PASS",
|
|
112
|
+
_RED_GREEN,
|
|
113
|
+
))
|
|
114
|
+
assert "S11" not in r.stderr
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
- [ ] **Step 3: valid plan fixture 보강**
|
|
118
|
+
|
|
119
|
+
`tests/fixtures/plans/valid_one_stage.md` 의 stage 1 섹션(`## 5.5.1 Stage 1:` 본문, Acceptance 라인 부근)에 한 줄 추가:
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
Conformance tests: stage-1 — qa/stage-1.ts (requires=[db])
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
`tests/fixtures/plans/valid_three_stage_parallel.md` 의 **각** stage 섹션(`## 5.5.1`/`## 5.5.2`/`## 5.5.3`)에 동일 형식으로 한 줄씩 추가(stageKey/경로만 stage 번호에 맞게).
|
|
126
|
+
|
|
127
|
+
- [ ] **Step 4: 전체 스위트로 ripple 파악 + 보강**
|
|
128
|
+
|
|
129
|
+
Run: `python3 -m pytest tests/ -q`
|
|
130
|
+
- 새로 깨지는 테스트는 "valid plan 인데 S11 누락" 류여야 한다. 각 깨진 테스트가 가리키는 valid plan(인라인 문자열 또는 fixture)에 stage 마다 `Conformance tests: ...` 한 줄을 추가한다. **assertion/로직은 건드리지 않는다.** invalid fixture(다른 코드 검사용)는 그대로 둔다(그들의 기대 코드는 여전히 존재).
|
|
131
|
+
- 반복해서 전부 green 이 될 때까지.
|
|
132
|
+
|
|
133
|
+
Run: `python3 -m pytest tests/test_validate_implementation_plan_stages.py -q` → S11 포함 전부 PASS
|
|
134
|
+
Run: `bash validators/validate-workflow.sh` → PASS
|
|
135
|
+
|
|
136
|
+
- [ ] **Step 5: 커밋**
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
git add validators/validate-implementation-plan-stages.py tests/test_validate_implementation_plan_stages.py tests/fixtures/plans/
|
|
140
|
+
# (+ ripple 로 수정된 다른 테스트 파일)
|
|
141
|
+
git commit -m "feat(validators): S11 — 모든 stage 의 conformance 선언/면제 강제"
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Task 2: implementation-planning prompt — 생성 계약
|
|
145
|
+
|
|
146
|
+
**Files:**
|
|
147
|
+
- Modify: `prompts/profiles/implementation-planning.md`
|
|
148
|
+
|
|
149
|
+
- [ ] **Step 1: stage 산출 계약 추가**
|
|
150
|
+
|
|
151
|
+
`prompts/profiles/implementation-planning.md` 의 Stage 구조 규칙(기존 `Slice value:` / `Acceptance:` / `### Stepwise Execution Order` / `TDD exemption:` 가 정의된 단락)에, 각 `## 5.5.<i> Stage` 섹션이 다음 중 하나를 포함하도록 추가한다:
|
|
152
|
+
|
|
153
|
+
- `Conformance tests:` 한 줄 — 이 stage 의 상위 요구사항(brief/requirements-discovery/error-analysis/improvement-discovery → Acceptance) 부합을 실DB·엔드포인트·외부 API 수준에서 검증하는 스크립트 선언. 그리고 planning 은 그 스크립트를 `<task_root>/qa/stage-<N>.<ext>` 에 작성하고 `<task_root>/qa/conformance-manifest.json` 의 entry(stageKey=`<task-id>-stage-<N>`, script, runCommand, requirementIds, requires∈{db,io,http,external}, passContract, exemption=null, waiver=null)를 추가한다. 스크립트 표준 인터페이스: `main → exit 0=PASS/≠0=FAIL`, stdout 마지막에 `QA-RESULT: PASS|FAIL` + `REQ <id>: PASS|FAIL: <근거>`.
|
|
154
|
+
- 또는 `Conformance exemption: <사유>` — db/io/http/external 을 건드리지 않거나 유닛으로 충분한 stage. (단 implementation diff 가 실제로 그 surface 를 건드리면 validate-run 의 diff-surface 교차검증이 BLOCKING — 면제로 숨길 수 없음.)
|
|
155
|
+
|
|
156
|
+
매니페스트 위치는 task-level `<task_root>/qa/` (planning→implementation→final-verification 공유). 산출물 경로 토큰은 `TASK_QA_PATH`.
|
|
157
|
+
|
|
158
|
+
- [ ] **Step 2: 강제 연계 명시(선언≠강제, Rule #3)**
|
|
159
|
+
|
|
160
|
+
같은 단락에 한 줄: "이 선언은 `validators/validate-implementation-plan-stages.py` 의 **S11** 이 stage 마다 강제하며, 매니페스트 JSON 구조는 `validate_conformance_manifest`(run/validate-run)가, 결과 게이트는 verifier Tier3 + validate-run 이 강제한다."
|
|
161
|
+
|
|
162
|
+
- [ ] **Step 3: 빌드 + workflow validator**
|
|
163
|
+
|
|
164
|
+
Run: `npm run build` (prompts → runtime sync)
|
|
165
|
+
Run: `bash validators/validate-workflow.sh` → PASS
|
|
166
|
+
Run: `python3 -m pytest tests/ -q` → 회귀 없음
|
|
167
|
+
|
|
168
|
+
- [ ] **Step 4: 커밋**
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
git add prompts/profiles/implementation-planning.md
|
|
172
|
+
git commit -m "feat(prompts/implementation-planning): stage conformance 스크립트+매니페스트 생성 계약"
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## Phase 4c Self-Review 체크
|
|
178
|
+
|
|
179
|
+
- [ ] S11: 모든 stage 가 `Conformance tests:` 또는 `Conformance exemption:` — 누락 시 BLOCKING?
|
|
180
|
+
- [ ] 기존 valid plan/테스트가 전부 green(최소 보강, assertion 불변)?
|
|
181
|
+
- [ ] planning prompt 가 매니페스트 위치(task-level)·스크립트 인터페이스·S11 연계를 명시?
|
|
182
|
+
- [ ] 전체 pytest + workflow validator 통과?
|
|
183
|
+
|
|
184
|
+
Phase 4c 완료 후 Phase 4d(verifier/final-verification Tier3 실행 prompt) 계획을 작성한다.
|