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.
Files changed (60) hide show
  1. package/docs/kr/architecture.md +10 -2
  2. package/docs/kr/cli.md +1 -1
  3. package/docs/project-structure-overview.md +1 -1
  4. package/docs/superpowers/plans/2026-06-02-final-verification-protocol-hardening.md +326 -0
  5. package/docs/superpowers/plans/2026-06-02-okstra-run-branch-confirm-step.md +337 -0
  6. package/docs/superpowers/plans/2026-06-02-okstra-run-phase-pane-cleanup.md +410 -0
  7. package/docs/superpowers/specs/2026-06-02-okstra-run-branch-confirm-step-design.md +113 -0
  8. package/docs/superpowers/specs/2026-06-02-okstra-run-phase-pane-cleanup-design.md +173 -0
  9. package/package.json +3 -2
  10. package/runtime/BUILD.json +2 -2
  11. package/runtime/{python → bin}/lib/okstra/usage.sh +3 -2
  12. package/runtime/bin/okstra-codex-exec.sh +3 -3
  13. package/runtime/bin/okstra-trace-cleanup.sh +64 -26
  14. package/runtime/prompts/profiles/_common-contract.md +9 -5
  15. package/runtime/prompts/profiles/final-verification.md +18 -16
  16. package/runtime/prompts/profiles/implementation-planning.md +1 -0
  17. package/runtime/prompts/wizard/prompts.ko.json +11 -0
  18. package/runtime/python/okstra_ctl/consumers.py +1 -1
  19. package/runtime/python/okstra_ctl/migrate.py +18 -6
  20. package/runtime/python/okstra_ctl/reconcile.py +2 -2
  21. package/runtime/python/okstra_ctl/render_final_report.py +0 -1
  22. package/runtime/python/okstra_ctl/run_context.py +9 -12
  23. package/runtime/python/okstra_ctl/wizard.py +70 -5
  24. package/runtime/python/okstra_ctl/worktree.py +74 -27
  25. package/runtime/schemas/final-report-v1.0.schema.json +34 -27
  26. package/runtime/skills/okstra-convergence/SKILL.md +1 -1
  27. package/runtime/skills/okstra-run/SKILL.md +2 -0
  28. package/runtime/templates/reports/final-report.template.md +24 -13
  29. package/runtime/templates/reports/final-verification-input.template.md +16 -5
  30. package/runtime/templates/reports/i18n/en.json +6 -3
  31. package/runtime/templates/reports/i18n/ko.json +6 -3
  32. package/runtime/templates/worker-prompt-preamble.md +7 -0
  33. package/runtime/validators/lib/fixtures.sh +2 -2
  34. package/runtime/validators/lib/validate-assets.sh +9 -0
  35. package/runtime/validators/validate-implementation-plan-stages.py +19 -11
  36. package/runtime/validators/validate-run.py +88 -0
  37. package/runtime/validators/validate-schedule.py +4 -4
  38. package/src/_proc.mjs +31 -0
  39. package/src/check-project.mjs +1 -25
  40. package/src/config.mjs +7 -31
  41. package/src/doctor.mjs +10 -29
  42. package/src/install.mjs +2 -10
  43. package/src/migrate.mjs +1 -18
  44. package/src/setup.mjs +1 -24
  45. /package/runtime/{python → bin}/lib/okstra/cli.sh +0 -0
  46. /package/runtime/{python → bin}/lib/okstra/globals.sh +0 -0
  47. /package/runtime/{python → bin}/lib/okstra/interactive.sh +0 -0
  48. /package/runtime/{python → bin}/lib/okstra/project-resolver.sh +0 -0
  49. /package/runtime/{python → bin}/lib/okstra-ctl/cmd-batch.sh +0 -0
  50. /package/runtime/{python → bin}/lib/okstra-ctl/cmd-list.sh +0 -0
  51. /package/runtime/{python → bin}/lib/okstra-ctl/cmd-open.sh +0 -0
  52. /package/runtime/{python → bin}/lib/okstra-ctl/cmd-projects.sh +0 -0
  53. /package/runtime/{python → bin}/lib/okstra-ctl/cmd-reconcile.sh +0 -0
  54. /package/runtime/{python → bin}/lib/okstra-ctl/cmd-reindex.sh +0 -0
  55. /package/runtime/{python → bin}/lib/okstra-ctl/cmd-rerun.sh +0 -0
  56. /package/runtime/{python → bin}/lib/okstra-ctl/cmd-show.sh +0 -0
  57. /package/runtime/{python → bin}/lib/okstra-ctl/cmd-tail.sh +0 -0
  58. /package/runtime/{python → bin}/lib/okstra-ctl/main.sh +0 -0
  59. /package/runtime/{python → bin}/lib/okstra-ctl/prepare.sh +0 -0
  60. /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 확인.