okstra 0.62.0 → 0.64.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 (36) hide show
  1. package/docs/kr/architecture.md +1 -1
  2. package/docs/superpowers/plans/2026-06-09-implementation-run-artifact-stage-isolation.md +320 -0
  3. package/docs/superpowers/plans/2026-06-10-lead-worker-completion-polling-PROBE.md +42 -0
  4. package/docs/superpowers/plans/2026-06-10-lead-worker-completion-polling.md +337 -0
  5. package/docs/superpowers/specs/2026-06-09-executor-model-custom-id-cascade-design.md +66 -0
  6. package/docs/superpowers/specs/2026-06-09-implementation-run-artifact-stage-isolation-design.md +87 -0
  7. package/docs/superpowers/specs/2026-06-10-lead-worker-completion-polling-design.md +113 -0
  8. package/package.json +1 -1
  9. package/runtime/BUILD.json +2 -2
  10. package/runtime/agents/SKILL.md +5 -2
  11. package/runtime/agents/TODO.md +9 -2
  12. package/runtime/agents/workers/claude-worker.md +7 -3
  13. package/runtime/agents/workers/codex-worker.md +6 -2
  14. package/runtime/agents/workers/gemini-worker.md +6 -2
  15. package/runtime/agents/workers/report-writer-worker.md +6 -1
  16. package/runtime/bin/lib/okstra-ctl/cmd-rerun.sh +23 -4
  17. package/runtime/prompts/profiles/implementation-planning.md +1 -1
  18. package/runtime/prompts/wizard/prompts.ko.json +17 -1
  19. package/runtime/python/okstra_ctl/backfill.py +23 -4
  20. package/runtime/python/okstra_ctl/consumers.py +118 -1
  21. package/runtime/python/okstra_ctl/paths.py +11 -0
  22. package/runtime/python/okstra_ctl/run.py +147 -67
  23. package/runtime/python/okstra_ctl/run_context.py +2 -0
  24. package/runtime/python/okstra_ctl/wizard.py +127 -29
  25. package/runtime/skills/okstra-convergence/SKILL.md +3 -1
  26. package/runtime/skills/okstra-report-writer/SKILL.md +2 -0
  27. package/runtime/skills/okstra-run/SKILL.md +1 -1
  28. package/runtime/skills/okstra-team-contract/SKILL.md +37 -0
  29. package/runtime/templates/reports/final-report.template.md +6 -9
  30. package/runtime/templates/reports/i18n/en.json +1 -1
  31. package/runtime/templates/reports/i18n/ko.json +1 -1
  32. package/runtime/templates/worker-prompt-preamble.md +10 -0
  33. package/runtime/validators/validate-implementation-plan-stages.py +10 -5
  34. package/runtime/validators/validate-run.py +20 -3
  35. package/src/install.mjs +21 -0
  36. package/src/uninstall.mjs +17 -17
@@ -352,7 +352,7 @@ okstra phase 는 PRD / issue file 을 직접 쓰지 않습니다. 동등한 결
352
352
 
353
353
  - `implementation`을 제외한 모든 phase는 source code edit, build, migration, deployment, 그 밖의 state-mutating 명령을 금지합니다(`final-verification`은 read-only 테스트 명령만 허용). `implementation`은 승인된 plan의 파일 목록 안에서만 edit/commit이 허용되며, `git push`·publish·deploy·실제 migration·third-party write API는 여전히 금지됩니다.
354
354
  - **모든 task-type 격리 worktree (BLOCKING)**: 모든 task-type 의 첫 번째 phase prepare 단계에서 `okstra-ctl` 이 자동으로 task-key 단위 `git worktree` 를 생성하고, 같은 task-key 의 이후 phase (`requirements-discovery` → `error-analysis` → `implementation-planning` → `implementation`) 는 동일한 worktree·브랜치를 재사용합니다. 위치는 `~/.okstra/worktrees/<project-id>/<task-group-segment>/<task-id-segment>/` (segment 의 `/`·`:` 등 특수문자는 `-` 로 정규화) 이고, 브랜치 이름은 `<work-category-prefix>-<task-id-segment>` (예: `feat-dev-9436`, `fix-dev-7311`) 입니다. base ref 는 첫 phase prepare 시점의 main worktree `HEAD`. `~/.okstra/worktrees/registry.json` (flock-guarded) 가 task-key → path/branch 매핑을 전역 관리해 동시 실행 시 path·branch 충돌을 방지합니다. configured sync dirs 는 main worktree 에서 symlink 로 연결되어 task checkout 사이의 filesystem continuity 를 제공합니다 (sync 대상 목록은 `project.json` 의 `worktreeSyncDirs` 또는 `OKSTRA_WORKTREE_SYNC_DIRS` 환경변수로 override 가능; 빈 배열이면 sync 비활성화). 이 sync 는 okstra context/write boundary 를 확장하지 않습니다. caller 가 이미 다른 worktree 안에 있거나 project_root 가 git repo 가 아니면 provisioning 은 skip 되고 executor 는 project_root 에서 그대로 작업합니다. worktree 는 run 종료 후 자동 삭제되지 않으며 후속 phase·PR 작성·rollback 검증의 권위 artefact 입니다. 수동 cleanup: `git -C <main-worktree> worktree remove <path>` → `git -C <main-worktree> branch -D <branch>` + registry 항목 삭제. 자세한 동작은 `prompts/profiles/implementation.md` 의 *Task worktree* 블록과 `agents/SKILL.md` 의 *Task worktree (BLOCKING for every task-type)* 섹션 참고.
355
- - **implementation stage 격리 worktree (동시 병렬)**: 위 task-key 단위 worktree 는 `requirements-discovery`~`implementation-planning` 의 모델입니다. `implementation` task 는 **stage 격리** 로 동작합니다 (spec `docs/superpowers/specs/2026-06-06-stage-worktree-isolation-design.md`) — **한 run = 한 stage**, 각 run 이 `.../<task-id-segment>/stage-<N>/` (브랜치 `<prefix>-<task-id-segment>-s<N>`) 격리 worktree 를 발급받습니다. registry 가 task-key 와 **stage-key** (`<task-key>#stage-<N>`) 를 함께 flock 예약하고, `_resolve_effective_stages` 가 `consumers.jsonl` 의 `started` + registry 예약 stage 를 ready 집합에서 제외하므로(점유 SSOT = registry), 사용자가 두 `implementation` run 을 동시에 띄우면 서로 다른 독립 stage 를 충돌 없이 진행합니다. base 결정: 독립 = 공통 anchor(첫 stage 진입 HEAD 고정), 단일 의존 = 선행 done commit, 다중 의존 = 선행이 모두 ancestor 인 task worktree HEAD(`git merge-base --is-ancestor`; 미머지 시 `PrepareError`). cost-aware-design 의 ready-set batch 는 stage 마다 격리 branch 가 필요해 의미를 잃으므로(같은 branch 에 두 stage-key reserve 시 branch-uniqueness 충돌) 폐기되었고, 순차 진행은 stage done 후 다음 run, 동시 진행은 별도 run 으로 — cost 등가. `--stage <auto|N>` 또는 wizard `stage_pick` 으로 stage 를 선택합니다.
355
+ - **implementation stage 격리 worktree (동시 병렬)**: 위 task-key 단위 worktree 는 `requirements-discovery`~`implementation-planning` 의 모델입니다. `implementation` task 는 **stage 격리** 로 동작합니다 (spec `docs/superpowers/specs/2026-06-06-stage-worktree-isolation-design.md`) — **한 run = 한 stage**, 각 run 이 `.../<task-id-segment>/stage-<N>/` (브랜치 `<prefix>-<task-id-segment>-s<N>`) 격리 worktree 를 발급받습니다. registry 가 task-key 와 **stage-key** (`<task-key>#stage-<N>`) 를 함께 flock 예약하고, `_resolve_effective_stages` 가 `consumers.jsonl` 의 `started` + registry 예약 stage 를 ready 집합에서 제외하므로(점유 SSOT = registry), 사용자가 두 `implementation` run 을 동시에 띄우면 서로 다른 독립 stage 를 충돌 없이 진행합니다. base 결정: 독립 = 공통 anchor(첫 stage 진입 HEAD 고정), 단일 의존 = 선행 done commit, 다중 의존 = 선행이 모두 ancestor 인 task worktree HEAD(`git merge-base --is-ancestor`; 미머지 시 `PrepareError`). cost-aware-design 의 ready-set batch 는 stage 마다 격리 branch 가 필요해 의미를 잃으므로(같은 branch 에 두 stage-key reserve 시 branch-uniqueness 충돌) 폐기되었고, 순차 진행은 stage done 후 다음 run, 동시 진행은 별도 run 으로 — cost 등가. `--stage <auto|N>` 또는 wizard `stage_pick` 으로 stage 를 선택합니다. worktree 뿐 아니라 **run 산출물(report·state·worker-results·manifest)도 `runs/implementation/stage-<N>/` 로 stage 별 격리**되므로 동시 실행하는 두 stage 의 보고서·상태가 섞이지 않습니다. 반면 `consumers.jsonl` 과 worktree registry 는 stage 간 공유되는 조율 SSOT 라 task-type 루트(`runs/implementation/`)에 그대로 둡니다.
356
356
  - `implementation` 과 `release-handoff` 를 제외한 모든 phase 는 source code edit, build, migration, deployment, 그 밖의 state-mutating 명령을 금지합니다 (`final-verification` 은 read-only 테스트 명령만 허용). `implementation` 은 승인된 plan 의 파일 목록 안에서만 edit/commit 이 허용되며, `git push`·publish·deploy·실제 migration·third-party write API 는 여전히 금지됩니다. `release-handoff` 는 source code 자체는 수정하지 않고, 사용자가 메뉴로 선택한 commit / push / PR 명령만 실행합니다 (force push, base 브랜치 직접 push, hook bypass, release publish 는 여전히 금지).
357
357
  - 사용자가 "다음 단계 진행해" 같은 표현을 보내도, 그 발화만으로 다음 phase가 자동 시작되지 않습니다. 다음 phase는 새 `okstra.sh` 실행으로만 시작합니다.
358
358
  - **Authority & permissions assumption (모든 task-type 및 `okstra-schedule` 공통)**: 사용자(및 팀)는 예상되는 모든 작업에 대해 완전한 권한·승인 권한을 보유한다고 가정합니다. 외부 승인, 서드파티 액세스, 역할/IAM 권한, 조직적 sign-off, 법무·보안 검토, 벤더 협의, "권한 보유 여부 확인" 같은 항목을 routing 결정·missing inputs·clarification questions·risk·dependency·open questions·effort/day 추정에 포함하지 않습니다. okstra 내부 phase 핸드오프(`implementation-planning`의 `approved:` frontmatter 등)는 사용자 본인이 즉시 승인 가능한 내부 게이트이므로 영향 없으며, `implementation`의 forbidden actions(`git push`, prod deploy, shared-DB migration 등)도 권한 사유가 아닌 **안전 사유**로 계속 적용됩니다.
@@ -0,0 +1,320 @@
1
+ # implementation run-산출물 stage 격리 — 활성화 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` run 의 산출물을 `runs/implementation/stage-<N>/` 로 격리해, 같은 task 의 독립 stage 를 동시 실행해도 보고서·state·worker-results 가 섞이지 않게 한다.
6
+
7
+ **Architecture:** foundation(`paths.py`/`run_context.py` 의 `stage` 파라미터, 이미 커밋 `baafa40`)은 `stage` 인자가 오면 `RUN_DIR=runs/implementation/stage-<N>` 를 만든다(현재 무동작). 본 plan 은 `prepare_task_bundle` 의 **stage 선택을 경로 계산 앞으로 옮겨** 그 stage 를 `compute_and_write_run_context` 에 전달해 활성화한다. 동시성 불변(`select == registry flock 예약` 원자성)을 보존하기 위해, stage 선택+base 결정+stage-worktree 예약(=registry flock)을 **하나의 함수로 묶어 경로 계산 전에 1회** 호출한다. `consumers.jsonl` 은 approved-plan 경로 기준이라 격리 대상이 아니다(공유 유지).
8
+
9
+ **Tech Stack:** Python (okstra_ctl), pytest(+ 실 git/flock 기반 e2e), okstra build(`npm run build`).
10
+
11
+ 설계 근거: [docs/superpowers/specs/2026-06-09-implementation-run-artifact-stage-isolation-design.md](../specs/2026-06-09-implementation-run-artifact-stage-isolation-design.md)
12
+
13
+ ---
14
+
15
+ ## File Structure
16
+
17
+ - Modify: `scripts/okstra_ctl/run.py` — `_reserve_implementation_stages` 를 (a) 경로-무관 **선택+예약+provision** 부분과 (b) `ctx` 의존 **wiring(EXECUTOR_WORKTREE_* 세팅 + consumers append)** 부분으로 분리. `prepare_task_bundle` 에서 (a)를 `compute_and_write_run_context` 앞으로 이동하고 selected stage 를 전달.
18
+ - Modify: `scripts/okstra_ctl/run_context.py` — (foundation 에서 이미 `stage` param 수용) 변경 없음 예상. 확인만.
19
+ - Test: `tests/test_e2e_impl_stage_artifact_isolation.py` (신규) — 동시 stage-1/stage-2 가 분리된 `stage-<N>/` 산출물 트리 + 분리 `lead-pane.id` + 공유 `consumers.jsonl` 을 갖는지.
20
+ - Test: `tests/test_okstra_ctl_paths.py` (기존 있으면 확장; 없으면 신규) — `compute_run_paths(task_type="implementation", stage=N)` 가 `runs/implementation/stage-N/...` 를 반환, `stage=None`/비-impl 은 불변.
21
+ - Modify(필요 시): `validators/validate-run.py`, `scripts/okstra_ctl/{render.py,report_views.py,context_cost.py,index.py,reconcile.py,backfill.py}` — `runs/implementation` hard-coded 경로가 있으면 stage-aware 로. (대부분 ctx/path-dict 경유라 무변경; Task 5 에서 grep 로 확정.)
22
+ - Modify: `CHANGES.md`, `docs/kr/architecture.md` — 사용자 영향 + 팀 lifecycle 절.
23
+
24
+ `runtime/` 는 빌드 출력 — source 수정 후 `npm run build`.
25
+
26
+ ---
27
+
28
+ ## Task 1: paths 단위 테스트로 foundation 동작 고정
29
+
30
+ **Files:**
31
+ - Test: `tests/test_okstra_ctl_paths.py`
32
+
33
+ - [ ] **Step 1: 실패/통과 테스트 작성**
34
+
35
+ `tests/test_okstra_ctl_paths.py` 에 추가(파일 없으면 신규 생성, 상단에 `from okstra_ctl.paths import compute_run_paths` + `from pathlib import Path`):
36
+
37
+ ```python
38
+ def _common(**over):
39
+ base = dict(
40
+ project_root=Path("/tmp/proj"), workspace_root=Path("/tmp/ws"),
41
+ project_id="p", task_group="g", task_id="t",
42
+ task_type="implementation", run_seq_override=1,
43
+ )
44
+ base.update(over)
45
+ return base
46
+
47
+
48
+ def test_implementation_stage_namespaces_run_dir():
49
+ ctx = compute_run_paths(stage=3, **_common())
50
+ assert ctx["RUN_DIR"].endswith("/runs/implementation/stage-3")
51
+ assert ctx["RUN_REPORTS_DIR"].endswith("/runs/implementation/stage-3/reports")
52
+ assert ctx["RUN_STATE_DIR"].endswith("/runs/implementation/stage-3/state")
53
+ assert ctx["RUN_STAGE"] == "3"
54
+
55
+
56
+ def test_implementation_without_stage_is_flat():
57
+ ctx = compute_run_paths(stage=None, **_common())
58
+ assert ctx["RUN_DIR"].endswith("/runs/implementation")
59
+ assert ctx["RUN_STAGE"] == ""
60
+
61
+
62
+ def test_non_implementation_ignores_stage():
63
+ ctx = compute_run_paths(stage=3, **_common(task_type="final-verification"))
64
+ assert ctx["RUN_DIR"].endswith("/runs/final-verification")
65
+ assert "stage-3" not in ctx["RUN_DIR"]
66
+ ```
67
+
68
+ - [ ] **Step 2: 실행해서 통과 확인** (foundation 이 이미 land 됨)
69
+
70
+ Run: `python3 -m pytest tests/test_okstra_ctl_paths.py -v`
71
+ Expected: 3개 PASS. 실패하면 foundation(`paths.py` stage 분기) 회귀이므로 먼저 수정.
72
+
73
+ - [ ] **Step 3: 커밋**
74
+
75
+ ```bash
76
+ git add tests/test_okstra_ctl_paths.py
77
+ git commit -m "test(paths): pin implementation stage-namespaced RUN_DIR"
78
+ ```
79
+
80
+ ---
81
+
82
+ ## Task 2: `_reserve_implementation_stages` 를 선택/예약 vs wiring 으로 분리
83
+
84
+ **Files:**
85
+ - Modify: `scripts/okstra_ctl/run.py` (`_reserve_implementation_stages` 1274–1386 영역)
86
+
87
+ 현재 함수는 한 덩어리로 (1) consumers/registry 읽기 → `_resolve_effective_stages` 선택, (2) base anchor + `_resolve_stage_base_commit` + `provision_stage_worktree`(registry 원자 예약), (3) `ctx["EXECUTOR_WORKTREE_*"]` 세팅 + `append_consumer(started)` 를 한다. (1)+(2)는 run-경로와 무관하지만 (3)은 `ctx` 가 필요하다.
88
+
89
+ - [ ] **Step 1: 선택+예약+provision 을 반환 객체로 추출**
90
+
91
+ `scripts/okstra_ctl/run.py` 에 신규 헬퍼 추가(`_reserve_implementation_stages` 위). `ctx` 대신 segment 값을 인자로 받아 **경로-무관**하게 만든다. (segment 는 `slugify_task_segment(inp.task_group/task_id)` 로 직접 도출 — `ctx` 불필요.)
92
+
93
+ ```python
94
+ from dataclasses import dataclass
95
+
96
+ @dataclass
97
+ class StageSelection:
98
+ stage: int
99
+ worktree_path: str
100
+ worktree_branch: str
101
+ worktree_base_ref: str
102
+ worktree_status: str
103
+ worktree_note: str
104
+ started_head_commit: str # consumers append 용 (degrade 경로는 HEAD sha)
105
+
106
+
107
+ def _select_and_provision_implementation_stage(
108
+ inp: "PrepareInputs", ctx_stage_map: list,
109
+ task_group_segment: str, task_id_segment: str, task_key: str,
110
+ executor_worktree_status: str,
111
+ ) -> StageSelection:
112
+ """run 경로 계산 전에 호출: ready stage 1개 선택 → (degrade 가 아니면)
113
+ base 결정 + stage worktree 발급(registry flock 으로 stage-key 원자 예약).
114
+ 선택과 예약이 한 호출에 묶여 동시성 불변(spec §2.3)을 보존한다.
115
+ consumers append 는 호출자가 ctx 확정 후 수행한다(반환값 사용)."""
116
+ from .consumers import read_consumers
117
+ from . import worktree as _worktree
118
+ from . import worktree_registry as _reg
119
+
120
+ plan_run_root = Path(inp.approved_plan_path).resolve().parents[1]
121
+ consumed = read_consumers(plan_run_root)
122
+ done_stages = {r["stage"] for r in consumed if r.get("status") == "done"}
123
+ started_stages = {r["stage"] for r in consumed if r.get("status") == "started"}
124
+ reserved_stages = _reg.list_active_stage_numbers(
125
+ inp.project_id, inp.task_group, inp.task_id,
126
+ )
127
+ batch = _resolve_effective_stages(
128
+ ctx_stage_map, done_stages, inp.stage,
129
+ started_stages=started_stages, reserved_stages=reserved_stages,
130
+ )
131
+ selected = batch[0]
132
+
133
+ # degradation(non-git / nested-worktree): worktree 미발급, consumers 만.
134
+ if executor_worktree_status.startswith("skipped"):
135
+ head = _git_out(inp.project_root, "rev-parse", "HEAD")
136
+ return StageSelection(selected, "", "", "", executor_worktree_status, "", head)
137
+
138
+ head_sha = _git_out(inp.project_root, "rev-parse", "HEAD")
139
+ if head_sha:
140
+ _reg.set_implementation_base(inp.project_id, inp.task_group, inp.task_id, head_sha)
141
+ anchor = _reg.get_implementation_base(inp.project_id, inp.task_group, inp.task_id) or ""
142
+ selected_stage = next(s for s in ctx_stage_map if s["stage_number"] == selected)
143
+ consumer_done_rows = [r for r in consumed if r.get("status") == "done"]
144
+ stage_base = _resolve_stage_base_commit(
145
+ selected_stage, consumer_done_rows, anchor_base_commit=anchor,
146
+ candidate_base=head_sha, project_root=Path(inp.project_root),
147
+ )
148
+ try:
149
+ prov = _worktree.provision_stage_worktree(
150
+ project_root=Path(inp.project_root), project_id=inp.project_id,
151
+ task_group_segment=task_group_segment, task_id_segment=task_id_segment,
152
+ work_category=inp.work_category, stage_number=selected, base_commit=stage_base,
153
+ )
154
+ except RuntimeError as exc:
155
+ raise PrepareError(f"stage worktree provisioning failed: {exc}") from exc
156
+ return StageSelection(
157
+ selected, prov.path, prov.branch, prov.base_ref, prov.status, prov.note, prov.base_ref,
158
+ )
159
+ ```
160
+
161
+ - [ ] **Step 2: wiring 헬퍼로 교체** — `_reserve_implementation_stages` 본문을 selection 결과를 ctx 에 반영 + consumers append 로 축소:
162
+
163
+ ```python
164
+ def _apply_implementation_stage(
165
+ inp: "PrepareInputs", ctx: dict, ctx_stage_map: list, sel: "StageSelection",
166
+ ) -> None:
167
+ """이미 선택·예약된 stage(sel)를 ctx 에 반영하고 consumers 에 started 기록."""
168
+ from .consumers import append_consumer
169
+ import datetime as _dt
170
+ ctx["parsed_stage_map"] = ctx_stage_map
171
+ ctx["effective_stages"] = [sel.stage]
172
+ ctx["EFFECTIVE_STAGES"] = str(sel.stage)
173
+ ctx["STAGE_BATCH_DIRECTIVE"] = (
174
+ f"- **Stage for this implementation run:** `{sel.stage}`. "
175
+ "Execute exactly this Stage Map stage — this is the authoritative scope. "
176
+ "Do NOT recompute from `consumers.jsonl`; the runtime already selected "
177
+ "and reserved this stage."
178
+ )
179
+ inp.stage = str(sel.stage)
180
+ if sel.worktree_status and not sel.worktree_status.startswith("skipped"):
181
+ ctx["EXECUTOR_WORKTREE_PATH"] = sel.worktree_path
182
+ ctx["EXECUTOR_WORKTREE_BRANCH"] = sel.worktree_branch
183
+ ctx["EXECUTOR_WORKTREE_BASE_REF"] = sel.worktree_base_ref
184
+ ctx["EXECUTOR_WORKTREE_STATUS"] = sel.worktree_status
185
+ ctx["EXECUTOR_WORKTREE_NOTE"] = sel.worktree_note
186
+ plan_run_root = Path(inp.approved_plan_path).resolve().parents[1]
187
+ now = _dt.datetime.now(_dt.timezone.utc).isoformat()
188
+ append_consumer(
189
+ plan_run_root, impl_task_key=ctx["TASK_KEY"], stage=sel.stage,
190
+ status="started", started_at=now, head_commit=sel.started_head_commit,
191
+ )
192
+ ```
193
+
194
+ - [ ] **Step 3: 정적 검사**
195
+
196
+ Run: `python3 -c "import ast; ast.parse(open('scripts/okstra_ctl/run.py').read()); print('ok')"`
197
+ Expected: `ok`
198
+
199
+ - [ ] **Step 4: 커밋**
200
+
201
+ ```bash
202
+ git add scripts/okstra_ctl/run.py
203
+ git commit -m "refactor(run): split impl stage select+reserve (path-independent) from ctx wiring"
204
+ ```
205
+
206
+ ---
207
+
208
+ ## Task 3: `prepare_task_bundle` reorder — stage 를 경로 계산 전에 선택
209
+
210
+ **Files:**
211
+ - Modify: `scripts/okstra_ctl/run.py` (`prepare_task_bundle` 1651–1810)
212
+
213
+ - [ ] **Step 1: 현재 흐름 확인** — `compute_and_write_run_context`(1686) 호출 직전에 `ctx_stage_map` 와 `inp.task_type == "implementation"` 가용한지, `EXECUTOR_WORKTREE_STATUS` 의 초기값(task worktree provision 결과)을 어디서 얻는지 read 로 확인. (degrade 신호가 task-worktree provision 에서 오면, stage select 는 그 신호가 필요 → task worktree provision 을 stage select 보다 먼저 두거나, stage select 안에서 자체 degrade 판정.)
214
+
215
+ - [ ] **Step 2: reorder 적용** — implementation 분기에서:
216
+ 1. `compute_and_write_run_context` **앞에서** `sel = _select_and_provision_implementation_stage(...)` 호출(`executor_worktree_status` 는 task-worktree provision 결과 또는 `provision_task_worktree` 의 사전 판정값 전달).
217
+ 2. `compute_and_write_run_context(..., stage=sel.stage)` 호출 → stage-namespaced ctx.
218
+ 3. ctx 생성 후 `_apply_implementation_stage(inp, ctx, ctx_stage_map, sel)` 로 wiring.
219
+ 4. 기존 `_reserve_implementation_stages(inp, ctx, ctx_stage_map)` 호출(1807) **제거**(위 1~3 이 대체). 비-implementation 경로는 불변.
220
+
221
+ > 핵심 불변: registry stage-key 예약은 `provision_stage_worktree`(sel 단계) **1회**만. 재선택/재예약 없음.
222
+
223
+ - [ ] **Step 3: 전체 단위 테스트**
224
+
225
+ Run: `python3 -m pytest tests/ -q`
226
+ Expected: 전부 PASS(2 skip 허용). 특히 `tests/test_e2e_multi_stage_q1_q9.py`, `tests/test_okstra_ctl_rerun_*` 회귀 없을 것. 실패 시 reorder 의 ctx_stage_map/worktree-status 전달 누락 점검.
227
+
228
+ - [ ] **Step 4: 커밋**
229
+
230
+ ```bash
231
+ git add scripts/okstra_ctl/run.py
232
+ git commit -m "feat(run): resolve implementation stage before run-path compute (activate stage isolation)"
233
+ ```
234
+
235
+ ---
236
+
237
+ ## Task 4: 동시 stage 격리 e2e
238
+
239
+ **Files:**
240
+ - Test: `tests/test_e2e_impl_stage_artifact_isolation.py` (신규)
241
+
242
+ - [ ] **Step 1: e2e 작성** — 기존 `tests/test_e2e_multi_stage_q1_q9.py` 의 픽스처(임시 git project + approved plan with ≥2 독립 stage)를 재사용/모사. 두 `prepare_task_bundle` 를 **순차 호출**(동시성은 flock 으로 직렬화되므로 순차 2회가 동치)해 각각 다른 ready stage 를 잡게 한 뒤:
243
+
244
+ ```python
245
+ def test_two_runs_isolate_stage_artifacts(tmp_impl_project):
246
+ out1 = prepare_task_bundle(tmp_impl_project.inputs(stage="auto"))
247
+ out2 = prepare_task_bundle(tmp_impl_project.inputs(stage="auto"))
248
+ d1, d2 = Path(out1.ctx["RUN_DIR"]), Path(out2.ctx["RUN_DIR"])
249
+ # 서로 다른 stage-<N> 트리
250
+ assert d1 != d2
251
+ assert d1.name.startswith("stage-") and d2.name.startswith("stage-")
252
+ # lead-pane.id 가 분리(부모 공유 아님)
253
+ assert d1.parent == d2.parent == (tmp_impl_project.task_root / "runs" / "implementation")
254
+ # consumers.jsonl 은 공유(plan-run-root 1개)
255
+ cons = tmp_impl_project.plan_run_root / "consumers.jsonl"
256
+ rows = [json.loads(l) for l in cons.read_text().splitlines() if l.strip()]
257
+ stages = sorted(r["stage"] for r in rows if r["status"] == "started")
258
+ assert stages == [out1.ctx["RUN_STAGE"] and int(out1.ctx["RUN_STAGE"]),
259
+ int(out2.ctx["RUN_STAGE"])] or len(set(stages)) == 2
260
+ ```
261
+
262
+ (픽스처 정확한 형태는 기존 e2e 의 헬퍼에 맞춰 작성 — 실행 시 `tests/test_e2e_multi_stage_q1_q9.py` 의 setup 함수를 import/모방.)
263
+
264
+ - [ ] **Step 2: 실행**
265
+
266
+ Run: `python3 -m pytest tests/test_e2e_impl_stage_artifact_isolation.py -v`
267
+ Expected: PASS. tmux/flock 불가 환경이면 skip 마커. 실패 시 두 run 이 같은 `RUN_DIR` 를 잡는지(=stage select 가 reorder 에서 실제 적용됐는지) 확인.
268
+
269
+ - [ ] **Step 3: 커밋**
270
+
271
+ ```bash
272
+ git add tests/test_e2e_impl_stage_artifact_isolation.py
273
+ git commit -m "test(e2e): concurrent implementation stages isolate run artifacts"
274
+ ```
275
+
276
+ ---
277
+
278
+ ## Task 5: reader/validator sweep + 문서 + 빌드
279
+
280
+ **Files:**
281
+ - Modify(조건부): `validators/validate-run.py`, `scripts/okstra_ctl/{render.py,report_views.py,context_cost.py,index.py,reconcile.py,backfill.py,workflow.py}`
282
+ - Modify: `CHANGES.md`, `docs/kr/architecture.md`
283
+
284
+ - [ ] **Step 1: hard-coded 경로 grep**
285
+
286
+ Run: `grep -rn "runs/implementation\|/runs/\" *+\|task_type_segment *== *.implementation" scripts/okstra_ctl/ validators/`
287
+ 점검: `runs/implementation` 를 **stage 없이 직접** 조립/스캔하는 곳을 찾는다. ctx/path-dict(`RUN_DIR`, `RUN_REPORTS_DIR` 등) 경유면 자동 흡수되어 무변경. 직접 조립이면 stage-aware 로 수정(예: timeline/index 가 `runs/<type>/reports/*` 글롭이면 `runs/<type>/**/reports/*` 로).
288
+
289
+ - [ ] **Step 2: 발견 건 수정 + 회귀 테스트** — 수정한 모듈별로 해당 테스트 재실행. 없으면 Step 1 결과를 `없음(모두 ctx 경유)` 으로 기록.
290
+
291
+ - [ ] **Step 3: 문서 + 빌드**
292
+
293
+ `CHANGES.md` 최상단:
294
+ ```
295
+ ## 2026-06-09 — implementation 산출물 stage 격리
296
+ - 변경: implementation run 산출물이 `runs/implementation/stage-<N>/` 로 stage 별 격리. stage 선택을 경로 계산 전에 수행하고 registry flock 으로 1회 예약.
297
+ - 사용자 영향: 같은 task 의 여러 stage 를 동시 실행해도 보고서·state·worker-results 가 섞이지 않는다. (무호환: 이전 `runs/implementation/` 루트 산출물은 자동 인식 안 됨 — 재실행 권장.)
298
+ ```
299
+
300
+ `docs/kr/architecture.md` 의 stage worktree 절에 "run-산출물도 `runs/implementation/stage-<N>/` 로 격리; consumers.jsonl·registry 는 공유" 1문장 추가.
301
+
302
+ Run: `cd <repo> && npm run build && python3 -m pytest tests/ -q && bash validators/validate-workflow.sh`
303
+ Expected: 빌드 24/24, 테스트 PASS, validator exit 0.
304
+
305
+ - [ ] **Step 4: 커밋**
306
+
307
+ ```bash
308
+ git add -A
309
+ git commit -m "docs(changes,architecture): implementation run-artifact stage isolation; reader sweep"
310
+ ```
311
+
312
+ ---
313
+
314
+ ## Self-Review (작성자 체크)
315
+
316
+ - **Spec coverage**: D1(stage-namespace RUN_DIR)=Task1+foundation, D2(seq in stage dir)=foundation(자동), D3(consumers 공유)=Task2/3 가 plan_run_root 유지 + Task4 e2e 가드, D4(lead-pane.id 격리)=Task4 e2e(부모 분리 확인), D5(degrade fallback)=Task2 `executor_worktree_status.startswith("skipped")` 분기. 전 D 매핑됨.
317
+ - **동시성 불변**: registry stage-key 예약은 `provision_stage_worktree` 1회만 — Task2 추출 + Task3 가 기존 `_reserve_implementation_stages` 호출 제거로 이중예약 차단.
318
+ - **Placeholder scan**: TBD/TODO 없음. Task4 픽스처는 기존 e2e 헬퍼 재사용 명시(코드 형태 제시).
319
+ - **식별자 일관성**: `StageSelection`, `_select_and_provision_implementation_stage`, `_apply_implementation_stage`, `stage` param 이 Task2/3/4 에서 동일.
320
+ - **알려진 한계**: 무호환(이전 루트 산출물), consumers/registry 공유는 spec 에 명시.
@@ -0,0 +1,42 @@
1
+ # Task 1 실측 결과 — self-scheduled 워커 완료 폴링
2
+
3
+ - 실측일: 2026-06-10
4
+ - 환경: interactive Claude Code 세션 (메인 lead 역할)
5
+ - 관련: [plan](2026-06-10-lead-worker-completion-polling.md) Task 1, [spec](../specs/2026-06-10-lead-worker-completion-polling-design.md) §5
6
+
7
+ ## 실험 1 — harness-tracked background 완료 → 정지 lead 자동 재개
8
+
9
+ - 절차: `Bash(run_in_background:true)` 로 "30초 후 결과 파일 작성 + DONE 출력" task(`bvdjebqzw`)를 띄우고, **ScheduleWakeup/cron 등 추가 장치 없이** lead 턴을 종료.
10
+ - 관측: 30초 뒤 background task 완료 시, **사용자 입력 없이** harness 가 `<task-notification>` 으로 lead 를 자동 재개시킴.
11
+ - 판정: **성공.** harness-tracked background 완료는 정지 lead 를 자동으로 깨운다. mailbox/idle 알림(누락 위험)에 의존하지 않는 신뢰성 있는 재개 채널이다.
12
+
13
+ ## 실험 2 — 복수 워커 단일 스케줄 until-loop 폴링
14
+
15
+ - 절차: 단일 `Bash(run_in_background:true)` task(`bh9mfrp3n`) 안에서 워커 2개를 시차(8초/22초)로 띄우고, 하나의 `until [ -f wA ] && [ -f wB ]; do sleep 3; done` 루프로 둘 다 대기.
16
+ - 관측: 출력 `BOTH WORKERS DETECTED at 02:55:50 — wA=A wB=B`. 단일 스케줄이 복수 워커 결과 파일을 모두 감지하고 완료 → 자동 재개.
17
+ - 판정: **성공.** 워커가 N개여도 단일 background 폴링이 전원의 결과 파일을 추적할 수 있다.
18
+
19
+ ## 메커니즘 확정 — `Bash run_in_background` + `until` 루프
20
+
21
+ cron / Monitor 가 아니라 **`Bash(run_in_background:true)` + `until`-조건 루프**가 채택 메커니즘이다.
22
+
23
+ 근거:
24
+ - Monitor 도구 설명이 단일 완료 알림("tell me when X is ready")에 대해 명시적으로 이 패턴을 권장한다: *"use Bash with run_in_background and a command that exits when the condition is true, e.g. `until grep -q "Ready in" dev.log; do sleep 0.5; done`"*. Monitor 는 **반복** 이벤트용, cron 은 **주기 재실행**용이라 단일 워커-완료 알림엔 과하다.
25
+ - background task 는 완료되면 사라지므로 spec §4.3 의 "스케줄 해제 / 좀비 cron 방지" 부담이 **원천 소멸**한다. 종료 lifecycle 이 cron 대비 훨씬 단순해진다.
26
+ - `until ...; do sleep N; done` 의 `sleep` 은 background 컨텍스트에서 허용된다 (foreground sleep 만 harness 가 차단). 실험 2 에서 `sleep 3` 으로 확인.
27
+
28
+ ## 게이트 판정
29
+
30
+ **통과.** 방향 C 채택. 메커니즘 = `Bash run_in_background` + `until` 루프.
31
+
32
+ ## 후속 반영 사항 (spec/plan 갱신 필요)
33
+
34
+ - spec §4.4 "Monitor vs cron" → "`Bash run_in_background` + `until` 루프" 로 확정. Monitor/cron 은 폴백/대안으로 격하.
35
+ - spec §4.3 종료 lifecycle → cron 해제 항목 제거(background task 자가 소멸). "deadline 초과" 는 until-loop 에 wall-clock 상한(`SECONDS` 가드)으로 구현. soft timeout 표는 유지.
36
+ - plan `<WAKEUP>` = `Bash run_in_background` + `until` 루프 폴링 스크립트.
37
+ - plan Task 7(self-wakeup 도구 권한 seed) → **불필요.** `Bash(run_in_background)` 는 이미 권한 내. settings.template.json 변경 없음.
38
+
39
+ ## 미확인 (정적 가정, 추가 실측 권장)
40
+
41
+ - 본 실측은 메인 interactive 세션. okstra.sh spawn 경로도 interactive([scripts/okstra.sh:173](../../../scripts/okstra.sh), `-p` 없음)이므로 동일 동작 기대하나, okstra.sh 가 띄운 lead 세션에서 background 자동 재개를 1회 교차 확인하면 완전.
42
+ - teammate(Agent) 워커가 비동기로 결과 파일을 쓰는 것 + background 폴링이 그 파일을 stat 으로 감지하는 것은 자명(기존 okstra 워커 동작 + 파일 존재 검사)하여 별도 실측은 생략. 워커 종류(in-process/teammate/CLI wrapper)와 폴링 메커니즘은 독립이다.