okstra 0.39.0 → 0.40.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.
- package/docs/kr/architecture.md +10 -2
- package/docs/kr/cli.md +1 -1
- package/docs/project-structure-overview.md +1 -1
- package/docs/superpowers/plans/2026-06-02-final-verification-protocol-hardening.md +326 -0
- package/docs/superpowers/plans/2026-06-02-okstra-run-branch-confirm-step.md +337 -0
- package/docs/superpowers/plans/2026-06-02-okstra-run-phase-pane-cleanup.md +410 -0
- package/docs/superpowers/specs/2026-06-02-okstra-run-branch-confirm-step-design.md +113 -0
- package/docs/superpowers/specs/2026-06-02-okstra-run-phase-pane-cleanup-design.md +173 -0
- package/package.json +3 -2
- package/runtime/BUILD.json +2 -2
- package/runtime/{python → bin}/lib/okstra/usage.sh +3 -2
- package/runtime/bin/okstra-codex-exec.sh +3 -3
- package/runtime/bin/okstra-trace-cleanup.sh +64 -26
- package/runtime/prompts/profiles/_common-contract.md +9 -5
- package/runtime/prompts/profiles/final-verification.md +18 -16
- package/runtime/prompts/profiles/implementation-planning.md +1 -0
- package/runtime/prompts/wizard/prompts.ko.json +11 -0
- package/runtime/python/okstra_ctl/consumers.py +1 -1
- package/runtime/python/okstra_ctl/migrate.py +18 -6
- package/runtime/python/okstra_ctl/reconcile.py +2 -2
- package/runtime/python/okstra_ctl/render_final_report.py +0 -1
- package/runtime/python/okstra_ctl/run_context.py +9 -12
- package/runtime/python/okstra_ctl/wizard.py +70 -5
- package/runtime/python/okstra_ctl/worktree.py +74 -27
- package/runtime/schemas/final-report-v1.0.schema.json +34 -27
- package/runtime/skills/okstra-convergence/SKILL.md +1 -1
- package/runtime/skills/okstra-run/SKILL.md +2 -0
- package/runtime/templates/reports/final-report.template.md +24 -13
- package/runtime/templates/reports/final-verification-input.template.md +16 -5
- package/runtime/templates/reports/i18n/en.json +6 -3
- package/runtime/templates/reports/i18n/ko.json +6 -3
- package/runtime/templates/worker-prompt-preamble.md +7 -0
- package/runtime/validators/lib/fixtures.sh +2 -2
- package/runtime/validators/lib/validate-assets.sh +9 -0
- package/runtime/validators/validate-implementation-plan-stages.py +19 -11
- package/runtime/validators/validate-run.py +88 -0
- package/runtime/validators/validate-schedule.py +4 -4
- package/src/_proc.mjs +31 -0
- package/src/check-project.mjs +1 -25
- package/src/config.mjs +7 -31
- package/src/doctor.mjs +10 -29
- package/src/install.mjs +2 -10
- package/src/migrate.mjs +1 -18
- package/src/setup.mjs +1 -24
- /package/runtime/{python → bin}/lib/okstra/cli.sh +0 -0
- /package/runtime/{python → bin}/lib/okstra/globals.sh +0 -0
- /package/runtime/{python → bin}/lib/okstra/interactive.sh +0 -0
- /package/runtime/{python → bin}/lib/okstra/project-resolver.sh +0 -0
- /package/runtime/{python → bin}/lib/okstra-ctl/cmd-batch.sh +0 -0
- /package/runtime/{python → bin}/lib/okstra-ctl/cmd-list.sh +0 -0
- /package/runtime/{python → bin}/lib/okstra-ctl/cmd-open.sh +0 -0
- /package/runtime/{python → bin}/lib/okstra-ctl/cmd-projects.sh +0 -0
- /package/runtime/{python → bin}/lib/okstra-ctl/cmd-reconcile.sh +0 -0
- /package/runtime/{python → bin}/lib/okstra-ctl/cmd-reindex.sh +0 -0
- /package/runtime/{python → bin}/lib/okstra-ctl/cmd-rerun.sh +0 -0
- /package/runtime/{python → bin}/lib/okstra-ctl/cmd-show.sh +0 -0
- /package/runtime/{python → bin}/lib/okstra-ctl/cmd-tail.sh +0 -0
- /package/runtime/{python → bin}/lib/okstra-ctl/main.sh +0 -0
- /package/runtime/{python → bin}/lib/okstra-ctl/prepare.sh +0 -0
- /package/runtime/{python → bin}/lib/okstra-ctl/usage.sh +0 -0
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
# okstra-run 브랜치 결정 확인 단계 구현 계획
|
|
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:** okstra-run wizard 의 최종 confirm 직전에 "새 worktree+브랜치 생성 vs 현재 worktree 재사용" 을 명시 확인하는 전용 `branch_confirm` 단계를 추가한다(동작 변경 없음, 확인 게이트만).
|
|
6
|
+
|
|
7
|
+
**Architecture:** `worktree.py` 의 provisioning 결정 로직을 부수효과 없는 `preview_worktree_decision()` 으로 추출하고 `provision_task_worktree` 가 이를 호출하게 리팩터한다(미리보기=실제 보장). wizard 에 predicate-driven `branch_confirm` Step 을 추가해 그 헬퍼로 결정을 미리보고 picker 로 확인받는다. 강제는 state 전이 구조(`confirm` 도달 전 `branch_confirm` 필수).
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** Python 3 (pytest), wizard prompts JSON(`prompts/wizard/prompts.ko.json`).
|
|
10
|
+
|
|
11
|
+
설계 근거: [docs/superpowers/specs/2026-06-02-okstra-run-branch-confirm-step-design.md](../specs/2026-06-02-okstra-run-branch-confirm-step-design.md)
|
|
12
|
+
|
|
13
|
+
핵심 사실(검증됨):
|
|
14
|
+
- okstra-run 스킬은 `--work-category` 를 넘기지 않으므로 wizard 경로의 work_category 는 `""` → 실제 브랜치는 `task-<task-id>` (`_work_category_prefix("")=="task"`). 미리보기도 `work_category=""` 로 동일 계산.
|
|
15
|
+
- wizard 는 predicate-driven: 각 `Step.applies(state)` 가 참인 첫 미답 Step 이 다음 단계. prompt 텍스트는 `prompts/wizard/prompts.ko.json` 의 `steps.<id>`.
|
|
16
|
+
- provision 결정 4갈래: not-git(`skipped-not-git`) / in-worktree(`skipped-in-worktree`) / registry active(`reused`) / 신규(`created`).
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## File Structure
|
|
21
|
+
|
|
22
|
+
- `scripts/okstra_ctl/worktree.py` — `WorktreeDecision` dataclass + `preview_worktree_decision()` 추출, `provision_task_worktree` 가 이를 사용하도록 리팩터.
|
|
23
|
+
- `scripts/okstra_ctl/wizard.py` — `S_BRANCH_CONFIRM` 상수, `WizardState.branch_confirmed`, `_build_branch_confirm`/`_submit_branch_confirm`, STEPS 항목, `_ready_for_confirm` 갱신.
|
|
24
|
+
- `prompts/wizard/prompts.ko.json` — `steps.branch_confirm` 텍스트.
|
|
25
|
+
- `docs/kr/architecture.md`, `CHANGES.md` — 새 단계 / 사용자 영향.
|
|
26
|
+
- tests: `tests/test_okstra_worktree.py`, `tests/test_okstra_ctl_wizard.py`, `tests/test_wizard_prompts.py`.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Task 1: preview_worktree_decision 추출 + provision 리팩터
|
|
31
|
+
|
|
32
|
+
**Files:**
|
|
33
|
+
- Modify: `scripts/okstra_ctl/worktree.py`
|
|
34
|
+
- Test: `tests/test_okstra_worktree.py`
|
|
35
|
+
|
|
36
|
+
- [ ] **Step 1: 실패 테스트 작성** (`tests/test_okstra_worktree.py` 에 추가)
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
def test_preview_decision_new_when_main_repo(tmp_path, monkeypatch):
|
|
40
|
+
import subprocess
|
|
41
|
+
from okstra_ctl.worktree import preview_worktree_decision
|
|
42
|
+
repo = tmp_path / "repo"; repo.mkdir()
|
|
43
|
+
subprocess.run(["git", "init", "-q"], cwd=repo, check=True)
|
|
44
|
+
monkeypatch.setenv("OKSTRA_HOME", str(tmp_path / "home"))
|
|
45
|
+
d = preview_worktree_decision(
|
|
46
|
+
project_root=repo, project_id="proj", task_group_segment="grp",
|
|
47
|
+
task_id_segment="dev-1", work_category="", base_ref="main",
|
|
48
|
+
)
|
|
49
|
+
assert d.status == "new"
|
|
50
|
+
assert d.branch == "task-dev-1"
|
|
51
|
+
assert d.base_ref == "main"
|
|
52
|
+
assert d.worktree_path.endswith("/proj/grp/dev-1")
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def test_preview_decision_not_git(tmp_path):
|
|
56
|
+
from okstra_ctl.worktree import preview_worktree_decision
|
|
57
|
+
d = preview_worktree_decision(
|
|
58
|
+
project_root=tmp_path, project_id="proj", task_group_segment="grp",
|
|
59
|
+
task_id_segment="dev-1", work_category="", base_ref="main",
|
|
60
|
+
)
|
|
61
|
+
assert d.status == "skipped-not-git"
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
- [ ] **Step 2: 실패 확인**
|
|
65
|
+
|
|
66
|
+
Run: `python3 -m pytest tests/test_okstra_worktree.py -k preview -q`
|
|
67
|
+
Expected: FAIL — `ImportError: cannot import name 'preview_worktree_decision'`
|
|
68
|
+
|
|
69
|
+
- [ ] **Step 3: WorktreeDecision + preview_worktree_decision 구현**
|
|
70
|
+
|
|
71
|
+
`worktree.py` 에 추가 (기존 `WorktreeProvision` dataclass 근처). decision 은 provision 의 4갈래를 부수효과 없이 계산한다. registry 재사용/in-worktree/not-git 분기는 기존 `provision_task_worktree` 의 가드(`_is_git_repo`, `_is_inside_non_main_worktree`, `worktree_registry.lookup`)를 그대로 호출(모두 read-only).
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
from dataclasses import dataclass
|
|
75
|
+
|
|
76
|
+
@dataclass
|
|
77
|
+
class WorktreeDecision:
|
|
78
|
+
status: str # "new" | "reused" | "skipped-in-worktree" | "skipped-not-git"
|
|
79
|
+
path: str # worktree path (new: prospective; reuse: existing; skip: project_root)
|
|
80
|
+
branch: str = "" # new: prospective branch; reused: existing branch
|
|
81
|
+
base_ref: str = "" # new: requested base_ref; reused: existing base
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def preview_worktree_decision(
|
|
85
|
+
*, project_root, project_id, task_group_segment, task_id_segment,
|
|
86
|
+
work_category, base_ref="",
|
|
87
|
+
) -> "WorktreeDecision":
|
|
88
|
+
"""Side-effect-free: what provision_task_worktree WOULD do, without touching disk.
|
|
89
|
+
|
|
90
|
+
Mirrors provision's decision branches exactly; reuses the same read-only
|
|
91
|
+
helpers so preview never diverges from the actual provisioning result.
|
|
92
|
+
"""
|
|
93
|
+
project_root = Path(project_root)
|
|
94
|
+
if not _is_git_repo(project_root):
|
|
95
|
+
return WorktreeDecision(status="skipped-not-git", path=str(project_root))
|
|
96
|
+
if _is_inside_non_main_worktree(project_root):
|
|
97
|
+
return WorktreeDecision(status="skipped-in-worktree", path=str(project_root))
|
|
98
|
+
safe_project = _safe_segment(project_id)
|
|
99
|
+
safe_group = _safe_segment(task_group_segment)
|
|
100
|
+
safe_task = _safe_segment(task_id_segment)
|
|
101
|
+
existing = worktree_registry.lookup(safe_project, safe_group, safe_task)
|
|
102
|
+
if existing is not None and existing.status == "active":
|
|
103
|
+
return WorktreeDecision(
|
|
104
|
+
status="reused", path=existing.worktree_path,
|
|
105
|
+
branch=existing.branch, base_ref=existing.base_ref,
|
|
106
|
+
)
|
|
107
|
+
return WorktreeDecision(
|
|
108
|
+
status="new",
|
|
109
|
+
path=str(compute_worktree_path(
|
|
110
|
+
project_id=safe_project, task_group_segment=safe_group,
|
|
111
|
+
task_id_segment=safe_task)),
|
|
112
|
+
branch=compute_branch_name(work_category=work_category, task_id_segment=safe_task),
|
|
113
|
+
base_ref=base_ref,
|
|
114
|
+
)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
- [ ] **Step 4: provision_task_worktree 가 헬퍼를 쓰도록 리팩터**
|
|
118
|
+
|
|
119
|
+
`provision_task_worktree` 의 결정 분기(현재 not-git / in-worktree / registry-lookup 구간, 약 line 494-532)를 `decision = preview_worktree_decision(...)` 한 줄 호출로 교체하고, `decision.status` 로 분기:
|
|
120
|
+
- `skipped-not-git` → 기존 note 로 `WorktreeProvision(status="skipped-not-git", ...)` 반환.
|
|
121
|
+
- `skipped-in-worktree` → 기존 note 로 반환.
|
|
122
|
+
- `reused` → `worktree_registry.touch_phase(...)` + `_seed_worktree_settings_symlink(Path(decision.path))` 후 기존 reused note 로 반환.
|
|
123
|
+
- `new` → `decision.branch` / `decision.path` 를 사용해 기존 생성 로직(`git worktree add` + `worktree_registry.reserve` + seed) 그대로 수행, `status="created"` 반환.
|
|
124
|
+
|
|
125
|
+
핵심: 결정(분기 판정 + branch/path 계산)은 헬퍼가 단일 소스. provision 은 실행만.
|
|
126
|
+
|
|
127
|
+
- [ ] **Step 5: 통과 + 회귀 확인**
|
|
128
|
+
|
|
129
|
+
Run: `python3 -m pytest tests/test_okstra_worktree.py -q`
|
|
130
|
+
Expected: 신규 preview 테스트 통과 + 기존 worktree 테스트 전부 통과(회귀 0).
|
|
131
|
+
|
|
132
|
+
- [ ] **Step 6: 커밋**
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
git add scripts/okstra_ctl/worktree.py tests/test_okstra_worktree.py
|
|
136
|
+
git commit -m "refactor(worktree): preview_worktree_decision 추출 + provision 공유"
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## Task 2: wizard branch_confirm 단계
|
|
142
|
+
|
|
143
|
+
**Files:**
|
|
144
|
+
- Modify: `scripts/okstra_ctl/wizard.py`
|
|
145
|
+
- Modify: `prompts/wizard/prompts.ko.json`
|
|
146
|
+
- Test: `tests/test_okstra_ctl_wizard.py`, `tests/test_wizard_prompts.py`
|
|
147
|
+
|
|
148
|
+
- [ ] **Step 1: 프롬프트 텍스트 추가** (`prompts/wizard/prompts.ko.json` 의 `steps` 객체에)
|
|
149
|
+
|
|
150
|
+
```json
|
|
151
|
+
"branch_confirm": {
|
|
152
|
+
"label": "{summary}",
|
|
153
|
+
"labels": {
|
|
154
|
+
"new": "새 브랜치 `{branch}` 를 base-ref `{base_ref}` 에서 새 worktree(`{path}`)에 생성합니다 — 진행할까요?",
|
|
155
|
+
"reuse": "현재 worktree(`{path}`, 브랜치 `{branch}`)를 재사용합니다 — 진행할까요?",
|
|
156
|
+
"in_worktree": "현재 worktree(`{path}`)에서 그대로 진행합니다(이미 non-main worktree) — 진행할까요?",
|
|
157
|
+
"not_git": "git 저장소가 아니므로 `{path}` 에서 직접 진행합니다 — 진행할까요?"
|
|
158
|
+
},
|
|
159
|
+
"options": { "proceed": "진행", "edit": "base-ref 다시 고르기" },
|
|
160
|
+
"echo_template": "branch-confirm: {value}"
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
- [ ] **Step 2: 실패 테스트 작성** — 핸들러 직접 테스트 (전체 wizard 구동 없이; 기존 파일의 `WizardState(...)` 직접 구성 + 핸들러 직접 호출 패턴을 따름. `_seed_wizard_prompts` 헬퍼는 파일 상단에 이미 있음)
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
def test_build_branch_confirm_new_worktree(tmp_path, monkeypatch):
|
|
168
|
+
import subprocess
|
|
169
|
+
from okstra_ctl.wizard import WizardState, _build_branch_confirm, S_BRANCH_CONFIRM
|
|
170
|
+
repo = tmp_path / "repo"; repo.mkdir()
|
|
171
|
+
subprocess.run(["git", "init", "-q"], cwd=repo, check=True)
|
|
172
|
+
_seed_wizard_prompts(repo)
|
|
173
|
+
monkeypatch.setenv("OKSTRA_HOME", str(tmp_path / "home"))
|
|
174
|
+
state = WizardState(
|
|
175
|
+
workspace_root=str(repo), project_root=str(repo),
|
|
176
|
+
project_id="proj", task_group="grp", task_id="dev-1", base_ref="main",
|
|
177
|
+
)
|
|
178
|
+
p = _build_branch_confirm(state)
|
|
179
|
+
assert p.step == S_BRANCH_CONFIRM and p.kind == "pick"
|
|
180
|
+
assert "task-dev-1" in p.label and "main" in p.label
|
|
181
|
+
vals = [o.value for o in p.options]
|
|
182
|
+
assert "proceed" in vals and "edit" in vals # new 모드 → edit 제공
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def test_submit_branch_confirm_proceed_and_edit(tmp_path):
|
|
186
|
+
from okstra_ctl.wizard import WizardState, _submit_branch_confirm
|
|
187
|
+
state = WizardState(
|
|
188
|
+
workspace_root=str(tmp_path), project_root=str(tmp_path),
|
|
189
|
+
project_id="proj", task_group="grp", task_id="dev-1", base_ref="main",
|
|
190
|
+
)
|
|
191
|
+
assert _submit_branch_confirm(state, "proceed") is not None
|
|
192
|
+
assert state.branch_confirmed is True
|
|
193
|
+
state.branch_confirmed = True
|
|
194
|
+
_submit_branch_confirm(state, "edit")
|
|
195
|
+
assert state.branch_confirmed is None # edit → 재확인 위해 리셋
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
(전이 자체 — `branch_confirm → confirm` 순서 — 는 기존 confirm-도달 테스트를 갱신해 검증한다. Step 5 참조.)
|
|
199
|
+
|
|
200
|
+
- [ ] **Step 3: 실패 확인**
|
|
201
|
+
|
|
202
|
+
Run: `python3 -m pytest tests/test_okstra_ctl_wizard.py -k branch_confirm -q`
|
|
203
|
+
Expected: FAIL (S_BRANCH_CONFIRM 미정의 / 전이 없음).
|
|
204
|
+
|
|
205
|
+
- [ ] **Step 4: wizard 구현** (`scripts/okstra_ctl/wizard.py`)
|
|
206
|
+
|
|
207
|
+
1) 상수: `S_CONFIRM` 정의 부근에 `S_BRANCH_CONFIRM = "branch_confirm"`.
|
|
208
|
+
2) `WizardState` 에 필드 추가: `branch_confirmed: Optional[bool] = None`.
|
|
209
|
+
3) build/submit 핸들러 추가:
|
|
210
|
+
|
|
211
|
+
```python
|
|
212
|
+
def _build_branch_confirm(state: WizardState) -> Prompt:
|
|
213
|
+
from okstra_ctl.worktree import preview_worktree_decision
|
|
214
|
+
decision = preview_worktree_decision(
|
|
215
|
+
project_root=Path(state.project_root),
|
|
216
|
+
project_id=state.project_id,
|
|
217
|
+
task_group_segment=state.task_group,
|
|
218
|
+
task_id_segment=state.task_id,
|
|
219
|
+
work_category="", # okstra-run 경로는 --work-category 를 안 넘김
|
|
220
|
+
base_ref=state.base_ref,
|
|
221
|
+
)
|
|
222
|
+
key = {
|
|
223
|
+
"new": "new", "reused": "reuse",
|
|
224
|
+
"skipped-in-worktree": "in_worktree", "skipped-not-git": "not_git",
|
|
225
|
+
}[decision.status]
|
|
226
|
+
t = _p(state.workspace_root, "branch_confirm")
|
|
227
|
+
label = t["labels"][key].format(
|
|
228
|
+
branch=decision.branch or "(none)",
|
|
229
|
+
base_ref=decision.base_ref or "(HEAD)",
|
|
230
|
+
path=decision.path,
|
|
231
|
+
)
|
|
232
|
+
# new 모드에서만 Edit 제공; 그 외에는 Proceed 만.
|
|
233
|
+
opts = t["options"]
|
|
234
|
+
options = [_opt("proceed", opts["proceed"])]
|
|
235
|
+
if decision.status == "new":
|
|
236
|
+
options.append(_opt("edit", opts["edit"]))
|
|
237
|
+
return Prompt(step=S_BRANCH_CONFIRM, kind="pick", label=label,
|
|
238
|
+
options=options, echo_template=t["echo_template"])
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def _submit_branch_confirm(state: WizardState, value: str) -> Optional[str]:
|
|
242
|
+
if value == "edit":
|
|
243
|
+
_reset_from(state, S_BASE_REF_PICK) # base-ref 재질문
|
|
244
|
+
state.branch_confirmed = None
|
|
245
|
+
return "branch-confirm: edit"
|
|
246
|
+
if value != "proceed":
|
|
247
|
+
raise WizardError(f"expected 'proceed' or 'edit', got: {value!r}")
|
|
248
|
+
state.branch_confirmed = True
|
|
249
|
+
return "branch-confirm: proceed"
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
4) STEPS 리스트에서 `Step(S_CONFIRM, ...)` **바로 앞**에 삽입:
|
|
253
|
+
|
|
254
|
+
```python
|
|
255
|
+
Step(S_BRANCH_CONFIRM,
|
|
256
|
+
applies=lambda s: _ready_for_confirm(s) and s.branch_confirmed is None,
|
|
257
|
+
build=_build_branch_confirm, submit=_submit_branch_confirm,
|
|
258
|
+
owns=("branch_confirmed",)),
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
5) `_ready_for_confirm` 가 confirm 전에 branch_confirm 을 강제하도록, `S_CONFIRM` 의 `applies` 를 `_ready_for_confirm(s) and s.branch_confirmed is True and s.confirmed is None` 로 변경.
|
|
262
|
+
|
|
263
|
+
> 주의: `applies` 술어가 `_ready_for_confirm(s)` 를 공유하므로, STEPS 리스트에서 `S_BRANCH_CONFIRM` 이 `S_CONFIRM` 보다 **앞에** 와야 branch_confirm 이 먼저 매칭된다(첫 매칭 우선). `_reset_from` 가 `branch_confirmed` 도 리셋 범위에 포함하는지 확인 — base-ref edit 시 branch_confirmed 가 다시 None 이어야 재계산된다(위 submit 에서 명시적으로 None 처리했으니 OK).
|
|
264
|
+
|
|
265
|
+
- [ ] **Step 5: 기존 confirm-도달 테스트 갱신 (필수 — 안 하면 회귀)**
|
|
266
|
+
|
|
267
|
+
`tests/test_okstra_ctl_wizard.py` 에서 `next_prompt(state).step == S_CONFIRM` 를 단언하는 기존 테스트들(현재 약 line 478, 519, 664, 685, 899 — `grep -n "== S_CONFIRM" tests/test_okstra_ctl_wizard.py` 로 전수 확인)은 이제 confirm **직전에 `S_BRANCH_CONFIRM` 이 먼저 나온다.** 각 지점을 다음 패턴으로 갱신:
|
|
268
|
+
|
|
269
|
+
```python
|
|
270
|
+
p = next_prompt(state)
|
|
271
|
+
assert p.step == S_BRANCH_CONFIRM # 새 게이트가 먼저
|
|
272
|
+
submit(state, "proceed")
|
|
273
|
+
assert next_prompt(state).step == S_CONFIRM
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
`S_BRANCH_CONFIRM` 을 테스트 상단 import 에 추가. 누락하면 해당 테스트들이 `S_BRANCH_CONFIRM != S_CONFIRM` 로 실패한다 — 전수 갱신할 것.
|
|
277
|
+
|
|
278
|
+
- [ ] **Step 6: 통과 + 회귀**
|
|
279
|
+
|
|
280
|
+
Run: `python3 -m pytest tests/test_okstra_ctl_wizard.py tests/test_wizard_prompts.py -q`
|
|
281
|
+
Expected: 신규 + 갱신된 기존 전부 통과. 이어서 `python3 -m pytest tests/ -q` 전체 회귀 0.
|
|
282
|
+
|
|
283
|
+
- [ ] **Step 7: 커밋**
|
|
284
|
+
|
|
285
|
+
```bash
|
|
286
|
+
git add scripts/okstra_ctl/wizard.py prompts/wizard/prompts.ko.json tests/test_okstra_ctl_wizard.py tests/test_wizard_prompts.py
|
|
287
|
+
git commit -m "feat(wizard): branch_confirm 단계 — 브랜치/worktree 결정 명시 확인"
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
## Task 3: okstra-run 스킬 안내 + 문서
|
|
293
|
+
|
|
294
|
+
**Files:**
|
|
295
|
+
- Modify: `skills/okstra-run/SKILL.md`
|
|
296
|
+
- Modify: `docs/kr/architecture.md`, `CHANGES.md`
|
|
297
|
+
|
|
298
|
+
- [ ] **Step 1: 스킬 1줄 안내** — `skills/okstra-run/SKILL.md` 의 pick 처리 설명 부근에, `branch_confirm` 도 일반 pick step 으로 그대로 렌더된다는 1줄 추가(별도 처리 불필요 명시). 기존 step 설명 형식을 따른다.
|
|
299
|
+
|
|
300
|
+
- [ ] **Step 2: architecture.md** — requirements-discovery fan-out 절 부근(또는 wizard/worktree 설명부)에 추가:
|
|
301
|
+
|
|
302
|
+
```markdown
|
|
303
|
+
#### branch_confirm (worktree 결정 확인)
|
|
304
|
+
|
|
305
|
+
okstra-run wizard 는 최종 confirm 직전에 `branch_confirm` 단계로 "새 브랜치/worktree 생성 vs
|
|
306
|
+
현재 worktree 재사용"을 명시 확인한다. 결정은 `worktree.preview_worktree_decision()`(부수효과 없음)
|
|
307
|
+
으로 미리보고, 같은 헬퍼를 `provision_task_worktree` 가 실행에 쓰므로 미리보기와 실제가 일치한다.
|
|
308
|
+
worktree 격리 동작 자체는 변경 없음(확인 게이트만).
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
- [ ] **Step 3: CHANGES.md** — `## 2026-06-02` 섹션에 추가:
|
|
312
|
+
|
|
313
|
+
```markdown
|
|
314
|
+
### feat(wizard): okstra-run 에 브랜치/worktree 결정 확인 단계 추가
|
|
315
|
+
|
|
316
|
+
- okstra-run wizard 가 최종 confirm 직전에 `branch_confirm` 단계를 띄워, 새 브랜치를 base-ref 에서
|
|
317
|
+
새 worktree 에 만드는지(또는 현재 worktree 재사용)를 명시 확인하게 함. auto mode 에서 base-ref 가
|
|
318
|
+
조용히 선택되어 의도치 않은 브랜치가 생기던 문제의 가시성을 높임. worktree 격리 동작은 변경 없음.
|
|
319
|
+
- 사용자 영향: 다음 release + `npx -y okstra@latest install` 후 적용. okstra-run 진행 시 브랜치 생성
|
|
320
|
+
여부를 확인하는 단계가 한 번 더 추가된다(full-auto 가 아닌 경우 실제로 멈춰 확인).
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
- [ ] **Step 4: 커밋**
|
|
324
|
+
|
|
325
|
+
```bash
|
|
326
|
+
git add skills/okstra-run/SKILL.md docs/kr/architecture.md CHANGES.md
|
|
327
|
+
git commit -m "docs(branch-confirm): 스킬 안내 + architecture + CHANGES"
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
## Task 4: 전체 회귀 + 빌드
|
|
333
|
+
|
|
334
|
+
- [ ] **Step 1:** `python3 -m pytest tests/ -q` → 전부 통과(회귀 0).
|
|
335
|
+
- [ ] **Step 2:** `npm run build` → 성공(runtime 동기화).
|
|
336
|
+
- [ ] **Step 3:** `node bin/okstra --version` → 버전 출력.
|
|
337
|
+
- [ ] **Step 4:** `git status --short` → clean 확인.
|