okstra 0.38.1 → 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/README.kr.md +1 -1
- package/README.md +1 -1
- package/docs/kr/architecture.md +18 -2
- package/docs/kr/cli.md +1 -1
- package/docs/project-structure-overview.md +2 -3
- 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/plans/2026-06-02-requirements-discovery-fanout.md +728 -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/docs/superpowers/specs/2026-06-02-requirements-discovery-fanout-design.md +154 -0
- package/docs/task-process/requirements-discovery.md +1 -1
- 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/profiles/requirements-discovery.md +18 -1
- package/runtime/prompts/wizard/prompts.ko.json +11 -0
- package/runtime/python/okstra_ctl/consumers.py +1 -1
- package/runtime/python/okstra_ctl/fanout.py +35 -0
- package/runtime/python/okstra_ctl/migrate.py +21 -42
- 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.py +0 -29
- package/runtime/python/okstra_ctl/run_context.py +9 -12
- package/runtime/python/okstra_ctl/seeding.py +0 -192
- package/runtime/python/okstra_ctl/wizard.py +70 -5
- package/runtime/python/okstra_ctl/work_categories.py +21 -0
- package/runtime/python/okstra_ctl/worktree.py +74 -77
- package/runtime/python/okstra_project/__init__.py +0 -6
- package/runtime/python/okstra_project/dirs.py +0 -8
- package/runtime/schemas/final-report-v1.0.schema.json +34 -27
- package/runtime/skills/okstra-context-loader/SKILL.md +1 -1
- package/runtime/skills/okstra-convergence/SKILL.md +1 -1
- package/runtime/skills/okstra-inspect/SKILL.md +1 -1
- package/runtime/skills/okstra-run/SKILL.md +2 -0
- package/runtime/templates/prd/brief.template.md +1 -1
- package/runtime/templates/reports/fan-out-unit.template.md +25 -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 +114 -0
- package/runtime/validators/validate-schedule.py +4 -4
- package/runtime/validators/validate_fanout.py +99 -0
- 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 +8 -36
- package/src/migrate.mjs +1 -18
- package/src/okstra-dirs.mjs +0 -11
- package/src/setup.mjs +1 -154
- package/src/uninstall.mjs +6 -13
- package/runtime/templates/okstra.CLAUDE.md +0 -104
- /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,113 @@
|
|
|
1
|
+
# okstra-run 브랜치 결정 확인 단계 설계
|
|
2
|
+
|
|
3
|
+
> 작성: 2026-06-02 · 상태: 설계 승인 대기
|
|
4
|
+
|
|
5
|
+
## 1. 배경 / 문제
|
|
6
|
+
|
|
7
|
+
`okstra-run` 은 모든 task-type 에서 task-key 마다 **새 git worktree + 브랜치**(base-ref 에서 분기)를
|
|
8
|
+
생성한다 ([scripts/okstra_ctl/run.py:730](../../../scripts/okstra_ctl/run.py) — "every phase,
|
|
9
|
+
every task-type"). 단 호출 시점에 이미 non-main worktree 안이면 그 worktree 를 재사용한다
|
|
10
|
+
([worktree.py:509](../../../scripts/okstra_ctl/worktree.py)).
|
|
11
|
+
|
|
12
|
+
문제: 이 "새 브랜치 vs 현재 worktree 재사용" 결정이 **암묵적으로 자동** 이루어진다. wizard 의
|
|
13
|
+
최종 confirm 요약에도 "새 브랜치 `<name>` 을 새 worktree 에 만든다" 는 사실이 드러나지 않는다.
|
|
14
|
+
Claude Code 를 auto mode 로 돌리면 base-ref(=`main` 추천)가 확인 없이 선택되고, 사용자가
|
|
15
|
+
의도하지 않은 브랜치가 main 에서 생성될 수 있다.
|
|
16
|
+
|
|
17
|
+
목표: wizard 중간에 **브랜치/worktree 결정을 명시적으로 보여주는 확인 단계**를 추가해, 무엇이
|
|
18
|
+
어디서 만들어지는지(또는 재사용되는지)를 사용자가 확인하게 한다.
|
|
19
|
+
|
|
20
|
+
## 2. 범위 / 결정 (brainstorming 합의)
|
|
21
|
+
|
|
22
|
+
| 항목 | 결정 |
|
|
23
|
+
|---|---|
|
|
24
|
+
| 성격 | **확인 게이트만** — worktree 격리 동작은 변경 없음 |
|
|
25
|
+
| 위치 | **전용 신설 step**, `base-ref` 직후 · 최종 `confirm` 직전 |
|
|
26
|
+
| 형태 | 일반 `pick` picker (`Proceed` 추천 / `Edit`) — full-auto 에서 auto-pick 가능성은 감수(사용자 선택) |
|
|
27
|
+
| 미리보기 정밀도 | **정확** — 계산된 브랜치명·base-ref·worktree 경로(또는 재사용 경로)를 명시 |
|
|
28
|
+
|
|
29
|
+
**비목표**: 현재 브랜치에서 격리 없이 실행하는 opt-out(동작 변경) · auto-pick 을 강제로
|
|
30
|
+
막는 하드스톱 · worker 로스터/ base-ref fail-closed (별도 가드레일, 이번 범위 아님).
|
|
31
|
+
|
|
32
|
+
## 3. 설계
|
|
33
|
+
|
|
34
|
+
### 3.1 부수효과 없는 미리보기 헬퍼 (single reference point)
|
|
35
|
+
|
|
36
|
+
`worktree.py` 의 provisioning 로직에서 **결정만 계산하고 디스크를 건드리지 않는** 순수 함수를
|
|
37
|
+
추출한다:
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
preview_worktree_decision(project_root, work_category, task_key_segments, base_ref)
|
|
41
|
+
-> { mode: "new" | "reuse", branch, worktree_path, base_ref }
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
- `mode` 는 provisioning 의 **두 재사용 경로를 모두 포섭**한다: ① cwd 가 이미 non-main
|
|
45
|
+
worktree([worktree.py:509](../../../scripts/okstra_ctl/worktree.py)), ② registry 에 이 task-key
|
|
46
|
+
의 active worktree 존재([worktree.py:519](../../../scripts/okstra_ctl/worktree.py)). 둘 중 하나면
|
|
47
|
+
`reuse`, 아니면 `new`.
|
|
48
|
+
- `branch` / `worktree_path` 는 기존 `compute_branch_name` / `compute_worktree_path` 재사용.
|
|
49
|
+
- `provision_task_worktree` 는 이 헬퍼를 호출해 결정을 얻도록 리팩터한다 → **wizard 미리보기와
|
|
50
|
+
실제 provisioning 이 같은 결정 로직을 공유** (미리보기/실제 불일치 불가).
|
|
51
|
+
- wizard 에는 이미 `state.reuse_worktree`(registry 기반, 위 ②만 감지 — [wizard.py:575,888](../../../scripts/okstra_ctl/wizard.py))
|
|
52
|
+
가 있다. 이 헬퍼가 ①+② 를 모두 포섭하므로, `branch_confirm` 의 mode 는 기존 플래그가 아니라
|
|
53
|
+
**헬퍼 결과**를 권위 소스로 쓴다(기존 `reuse_worktree` 는 헬퍼로 대체하거나 헬퍼에서 파생).
|
|
54
|
+
|
|
55
|
+
### 3.2 wizard state `branch_confirm`
|
|
56
|
+
|
|
57
|
+
위치: `base_ref`(_pick/_text) 통과 직후 → `confirm` 직전.
|
|
58
|
+
|
|
59
|
+
- emit: `kind: "pick"`, `multi: false`. 결정 텍스트는 step 의 `label` 에 담는다(별도 summary-fetch
|
|
60
|
+
서브커맨드 불필요).
|
|
61
|
+
- **mode=new**: 옵션 `Proceed`(추천) / `Edit`. label:
|
|
62
|
+
"새 브랜치 `<branch>` 를 base-ref `<X>` 에서 새 worktree(`<worktree_path>`)에 생성 — 진행?"
|
|
63
|
+
- **mode=reuse**: 옵션 `Proceed` 만. label:
|
|
64
|
+
"현재 worktree `<worktree_path>` (브랜치 `<branch>`) 재사용 — 진행?"
|
|
65
|
+
- 전이:
|
|
66
|
+
- `Proceed` → `confirm`
|
|
67
|
+
- `Edit`(mode=new 만) → `base_ref` 로 복귀(base-ref 재선택 시 branch_confirm 재계산)
|
|
68
|
+
- 데이터 의존: 헬퍼는 `work_category` + task-key 세그먼트가 필요. wizard state 가 이를 직접
|
|
69
|
+
보유하지 않으면 끌어오는 작업을 구현에 포함(없으면 base-ref 만으로 "새 worktree 생성" 수준
|
|
70
|
+
문구로 graceful degrade — 단 정확성 목표상 threading 우선).
|
|
71
|
+
|
|
72
|
+
### 3.3 재사용 케이스 + base-ref 스킵 정합
|
|
73
|
+
|
|
74
|
+
헬퍼의 `mode == reuse` 면 base-ref 는 무의미하다. wizard 의 기존 base-ref 게이팅(현재
|
|
75
|
+
`state.reuse_worktree` 로 registry-reuse 케이스를 다룸)이 cwd-in-worktree 케이스도 함께 커버하도록
|
|
76
|
+
헬퍼 mode 기준으로 정렬한다. `branch_confirm` 은 mode 만 보고 옵션/문구를 구성한다(reuse →
|
|
77
|
+
Proceed-only + 재사용 문구, new → Proceed/Edit + 생성 문구). base-ref step 의 정확한 스킵 조건은
|
|
78
|
+
구현 시 `state.reuse_worktree`/헬퍼 mode 기준으로 확인·정렬.
|
|
79
|
+
|
|
80
|
+
### 3.4 okstra-run skill
|
|
81
|
+
|
|
82
|
+
`branch_confirm` 은 일반 `pick` step 이라 okstra-run 의 기존 generic pick 렌더(AskUserQuestion)가
|
|
83
|
+
그대로 처리한다. 결정 텍스트가 `label` 에 들어오므로 **스킬 변경은 없음(있어도 안내 1줄)**.
|
|
84
|
+
|
|
85
|
+
## 4. enforcement
|
|
86
|
+
|
|
87
|
+
validator 계약이 아니라 **state machine 구조**가 강제한다 — `confirm` 에 도달하려면
|
|
88
|
+
`branch_confirm` 을 반드시 통과해야 한다(state 전이로 보장). 별도 validator 불필요.
|
|
89
|
+
|
|
90
|
+
## 5. 변경 표면
|
|
91
|
+
|
|
92
|
+
| 영역 | 파일 | 변경 |
|
|
93
|
+
|---|---|---|
|
|
94
|
+
| 미리보기 헬퍼 | `scripts/okstra_ctl/worktree.py` | `preview_worktree_decision` 추출 + `provision_task_worktree` 가 이를 호출하도록 리팩터 |
|
|
95
|
+
| wizard state | `scripts/okstra_ctl/wizard.py` | `branch_confirm` state + 빌드/제출 핸들러 + `base_ref → branch_confirm → confirm` 전이, `Edit→base_ref` |
|
|
96
|
+
| skill | `skills/okstra-run/SKILL.md` | (선택) generic pick 이 새 step 을 처리함을 1줄 명시 |
|
|
97
|
+
| 테스트 | 기존 wizard 테스트 모듈 + worktree 테스트 | 아래 §6 |
|
|
98
|
+
| 문서 | `docs/kr/architecture.md`, `CHANGES.md` | 새 단계 / 사용자 영향 |
|
|
99
|
+
|
|
100
|
+
## 6. 테스트 전략
|
|
101
|
+
|
|
102
|
+
- **미리보기 헬퍼 순수 단위테스트**: 메인 cwd → `mode=new` + 올바른 branch/base, non-main
|
|
103
|
+
worktree cwd → `mode=reuse`.
|
|
104
|
+
- **공유 회귀**: `provision_task_worktree` 가 헬퍼와 동일 결정을 내는지(미리보기=실제).
|
|
105
|
+
- **wizard 전이**: `base_ref → branch_confirm → confirm`; mode=new 프롬프트에 계산된 branch+base
|
|
106
|
+
노출; mode=reuse 프롬프트에 worktree path 노출 + Proceed-only; `Proceed→confirm` /
|
|
107
|
+
`Edit→base_ref`.
|
|
108
|
+
|
|
109
|
+
## 7. 비목표 (이번 범위 아님)
|
|
110
|
+
|
|
111
|
+
- 현재 브랜치 격리 opt-out(동작 변경).
|
|
112
|
+
- auto-pick 하드스톱(추천 없는 강제 입력).
|
|
113
|
+
- worker 로스터 핀(project.json `defaultWorkers`) · base-ref fail-closed — 별도 가드레일 작업.
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# okstra-run phase 전환 시 tmux pane 정리 — 설계
|
|
2
|
+
|
|
3
|
+
## Index
|
|
4
|
+
|
|
5
|
+
- [배경 / 문제](#배경--문제)
|
|
6
|
+
- [현재 메커니즘 (검증된 사실)](#현재-메커니즘-검증된-사실)
|
|
7
|
+
- [목표 / 비목표](#목표--비목표)
|
|
8
|
+
- [설계 결정](#설계-결정)
|
|
9
|
+
- [구현 범위](#구현-범위)
|
|
10
|
+
- [닫을 대상 — title allowlist](#닫을-대상--title-allowlist)
|
|
11
|
+
- [스크립트 변경 (`okstra-trace-cleanup.sh`)](#스크립트-변경-okstra-trace-cleanupsh)
|
|
12
|
+
- [contract 변경 (`_common-contract.md`)](#contract-변경-_common-contractmd)
|
|
13
|
+
- [테스트](#테스트)
|
|
14
|
+
- [enforcement 정직성 / 알려진 한계](#enforcement-정직성--알려진-한계)
|
|
15
|
+
- [영향 / 전파](#영향--전파)
|
|
16
|
+
|
|
17
|
+
## 배경 / 문제
|
|
18
|
+
|
|
19
|
+
okstra-run 진행 중 lead 가 내부 phase 마다 새로운 worker 를 dispatch 하면 tmux pane 이
|
|
20
|
+
누적된다. 한 run 이 끝나도 이전 phase 의 worker pane 들이 좀비로 남아 화면을 채운다.
|
|
21
|
+
실제 지난 run 에서 `claude-worker ×3`, `codex-worker ×3`, `report-writer-worker ×1`
|
|
22
|
+
= 7개 pane(`%133`~`%139`)이 run 종료 후에도 남아 사용자가 수동으로 `tmux kill-pane`
|
|
23
|
+
해야 했다.
|
|
24
|
+
|
|
25
|
+
현재 contract 는 **trace pane 만**, 그것도 **run 맨 끝 1회**만 정리하고, worker-agent
|
|
26
|
+
pane 은 전혀 관리하지 않는다.
|
|
27
|
+
|
|
28
|
+
## 현재 메커니즘 (검증된 사실)
|
|
29
|
+
|
|
30
|
+
okstra dispatch 1회당 **두 종류**의 pane 이 생긴다:
|
|
31
|
+
|
|
32
|
+
1. **worker-agent pane** — lead 가 `claude-worker` / `codex-worker` / `gemini-worker` /
|
|
33
|
+
`report-writer-worker` 를 Agent(서브에이전트)로 dispatch 하면, Claude Code 하네스의
|
|
34
|
+
서브에이전트 실행이 각 백그라운드 에이전트마다 pane 을 부여한다. title 은 에이전트
|
|
35
|
+
이름(하네스가 ✳/💬 glyph prefix 를 붙임). **okstra 코드가 만들지 않고 registry 에도
|
|
36
|
+
없으며 run 종료 후에도 잔존**한다. 누적의 주범.
|
|
37
|
+
2. **trace pane** — codex/gemini wrapper(`okstra-codex-exec.sh` /
|
|
38
|
+
`okstra-gemini-exec.sh`)가 worker-agent pane **안에서** `tmux split-window` 로 만드는
|
|
39
|
+
`tail -F <log>` panel. title `<cli>-<role>-<pid>-trace`. okstra 소유이고 caller
|
|
40
|
+
`$TMUX_PANE` 으로 키된 registry(`${TMPDIR:-/tmp}/okstra-trace-panes/<caller>.list`)에
|
|
41
|
+
등록되며 `okstra-trace-cleanup.sh` 가 정리한다. `SessionEnd` 훅이 `/exit` 시 호출
|
|
42
|
+
([settings.template.json:156](../../../templates/reports/settings.template.json)).
|
|
43
|
+
|
|
44
|
+
사용자가 기억한 "codex/gemini = 새 panel 에 로그 tail" 설계는 **(2) trace pane 으로
|
|
45
|
+
실재**한다([architecture.md Live-log mirror 절](../../kr/architecture.md)). 문제는 그
|
|
46
|
+
위에 얹힌 **(1) worker-agent pane** 이 정리되지 않는 것이다.
|
|
47
|
+
|
|
48
|
+
## 목표 / 비목표
|
|
49
|
+
|
|
50
|
+
### 목표
|
|
51
|
+
- 새 phase 의 worker 를 dispatch 하기 직전, 이전 phase 의 okstra pane(에이전트 + trace)을
|
|
52
|
+
자동으로 모두 닫는다.
|
|
53
|
+
- lead 자신의 pane 과 사용자의 무관한 pane 은 절대 건드리지 않는다.
|
|
54
|
+
- pane 정리 구현을 단일 참조점(`okstra-trace-cleanup.sh`)으로 유지한다.
|
|
55
|
+
|
|
56
|
+
### 비목표
|
|
57
|
+
- codex/gemini 의 실행 구조 변경(에이전트 dispatch → wrapper 직접 실행)은 하지 **않는다**.
|
|
58
|
+
구조(에이전트 pane + tail panel)는 그대로 두고 정리만 한다. (사용자 결정: "phase 시작 시
|
|
59
|
+
전부 닫기만")
|
|
60
|
+
- 새 lifecycle phase 자동 진행 등 anti-escalation 규칙 변경 없음.
|
|
61
|
+
|
|
62
|
+
## 설계 결정
|
|
63
|
+
|
|
64
|
+
| # | 결정 | 사용자 확인 |
|
|
65
|
+
|---|------|-------------|
|
|
66
|
+
| D1 | 정리 시점 = **내부 phase 시작 직전** (이전 phase pane 전부) | ✅ "phase 시작 시 전부 닫기" |
|
|
67
|
+
| D2 | phase 경계 정리는 **자동(무콜)**, 한 줄 보고 | ✅ "자동 정리 (무콜)" |
|
|
68
|
+
| D3 | run 최종 종료 시점만 기존 **이진 prompt** 유지 | ✅ "마지막만 기존 prompt 유지" |
|
|
69
|
+
| D4 | 닫을 대상 = worker-agent pane + trace pane | ✅ (로그의 7개 pane) |
|
|
70
|
+
| D5 | 구조 변경 없음(에이전트 pane 제거 안 함) | ✅ "phase 시작 시 전부 닫기만" |
|
|
71
|
+
|
|
72
|
+
## 구현 범위
|
|
73
|
+
|
|
74
|
+
1. `scripts/okstra-trace-cleanup.sh` — "okstra pane" 식별을 **registry trace pane ∪
|
|
75
|
+
title-allowlist worker-agent pane (lead pane 제외)** 로 통일.
|
|
76
|
+
2. `prompts/profiles/_common-contract.md` — phase 시작 자동정리 규약 추가 + 최종 prompt
|
|
77
|
+
대상 통합.
|
|
78
|
+
3. `tests/test_okstra_trace_cleanup.py` — title-allowlist kill / lead·비-okstra pane
|
|
79
|
+
보존 회귀 가드 추가.
|
|
80
|
+
4. 문서: `docs/kr/architecture.md` Live-log mirror 절, `CHANGES.md` 갱신.
|
|
81
|
+
|
|
82
|
+
## 닫을 대상 — title allowlist
|
|
83
|
+
|
|
84
|
+
rule #7 (allowlist over denylist): **닫아도 되는 것만** 명시.
|
|
85
|
+
|
|
86
|
+
- worker-agent pane: title 이 다음 중 하나를 **포함**:
|
|
87
|
+
`claude-worker`, `codex-worker`, `gemini-worker`, `report-writer-worker`
|
|
88
|
+
(하네스 glyph/공백 prefix 를 흡수하기 위해 부분일치).
|
|
89
|
+
- trace pane: caller `$TMUX_PANE` registry 에 등록된 pane id (기존 메커니즘 그대로).
|
|
90
|
+
|
|
91
|
+
**불변 안전장치**:
|
|
92
|
+
- lead 자신의 pane(`$TMUX_PANE`)은 식별 결과에서 **항상 제외** — title 이 우연히 매칭돼도
|
|
93
|
+
제외.
|
|
94
|
+
- 검색 범위는 lead pane 이 속한 **tmux 세션**으로 한정(`tmux list-panes -s -t
|
|
95
|
+
$TMUX_PANE`), 전체 세션(`-a`) 스캔 금지.
|
|
96
|
+
- allowlist 에 없는 title 은 절대 kill 대상이 아니다.
|
|
97
|
+
|
|
98
|
+
## 스크립트 변경 (`okstra-trace-cleanup.sh`)
|
|
99
|
+
|
|
100
|
+
식별 함수를 도입해 두 소스를 합집합으로 모은다 (의사 흐름):
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
okstra_panes():
|
|
104
|
+
panes = registry 에 등록된 pane id 들 # 기존 trace 경로
|
|
105
|
+
for (pane_id, title) in (현재 세션의 모든 pane): # tmux list-panes -s -t $TMUX_PANE
|
|
106
|
+
if pane_id == $TMUX_PANE: continue # lead 제외 (불변)
|
|
107
|
+
if title 가 allowlist 키워드 포함: panes += pane_id
|
|
108
|
+
return dedupe(panes)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
- `--list` → `okstra_panes()` 의 각 `<pane_id>\t<title>` 출력. (최종 wrap-up prompt 가
|
|
112
|
+
사용; 비어 있으면 빈 stdout.)
|
|
113
|
+
- (무인자) kill → `okstra_panes()` 전부 `tmux kill-pane` + registry 파일 제거. (phase
|
|
114
|
+
시작 자동정리 / 최종 prompt 예-분기 / `SessionEnd` 훅 공용.)
|
|
115
|
+
- 기존 동작 보존: `$TMUX_PANE` 미설정 → no-op(exit 0). 모든 tmux 실패는 silent degrade
|
|
116
|
+
(lead 종료를 막지 않음).
|
|
117
|
+
- `--dry-run`(= `--list` alias), `-h/--help`, unknown-option exit 2 는 유지.
|
|
118
|
+
|
|
119
|
+
trace registry 의 caller-scoping 격리(다른 Claude 인스턴스의 trace pane 미터치)는 그대로
|
|
120
|
+
유지된다. worker-agent pane 은 registry 가 없어 title 로만 식별하므로 세션 범위로만 좁힌다
|
|
121
|
+
([아래 한계](#enforcement-정직성--알려진-한계) 참고).
|
|
122
|
+
|
|
123
|
+
## contract 변경 (`_common-contract.md`)
|
|
124
|
+
|
|
125
|
+
기존 "Phase wrap-up — worker trace pane disposition" 섹션에 두 가지를 반영:
|
|
126
|
+
|
|
127
|
+
1. **신규 — phase 시작 자동정리 (shared, BLOCKING, 모든 profile 의 내부 phase 공통)**:
|
|
128
|
+
lead 가 새 worker batch 를 dispatch 하기 **직전**(이미 PROGRESS 마커를 찍는 전환점):
|
|
129
|
+
- 각 `PROGRESS: phase-5.5-convergence round=<N>` 직전
|
|
130
|
+
- `PROGRESS: phase-6-synthesis dispatching report-writer-worker` 직전
|
|
131
|
+
`$TMUX_PANE` 가 셋이면 `okstra-trace-cleanup.sh`(무인자)를 **prompt 없이** 실행하고
|
|
132
|
+
`이전 phase okstra pane N개 정리` 한 줄만 보고. `$TMUX_PANE` 미설정이면 silent-skip,
|
|
133
|
+
합성 pane 목록 날조 금지(기존 규칙과 동일).
|
|
134
|
+
2. **최종 prompt 대상 통합**: 기존 run-종료 이진 prompt 의 `--list`/kill 이 trace pane
|
|
135
|
+
뿐 아니라 worker-agent pane(예: 마지막 phase 의 report-writer)까지 포함하도록 확장.
|
|
136
|
+
prompt 문구·이진 선택(모두 닫기/그대로)·empty-skip 규칙은 그대로.
|
|
137
|
+
|
|
138
|
+
`_common-contract.md` 헤더의 "phase-specific 규칙 금지"와 충돌하지 않는다 — Phase 4/5,
|
|
139
|
+
5.5, 6 은 이미 같은 파일의 "Worker interaction model" 절이 공유 lifecycle 로 다루는
|
|
140
|
+
대상이다.
|
|
141
|
+
|
|
142
|
+
## 테스트
|
|
143
|
+
|
|
144
|
+
`tests/test_okstra_trace_cleanup.py` 에 실 tmux 서버 기반 케이스 추가(기존 픽스처 재사용):
|
|
145
|
+
|
|
146
|
+
- `test_kills_worker_agent_panes_by_title`: caller(lead) pane + `claude-worker` /
|
|
147
|
+
`codex-worker` / `report-writer-worker` title pane 생성 → kill 모드 실행 → 에이전트
|
|
148
|
+
pane 은 죽고 **caller pane 은 보존**.
|
|
149
|
+
- `test_preserves_non_okstra_panes`: allowlist 외 title(예: `가격 계산 기능 개발`,
|
|
150
|
+
`vim`) pane 은 보존.
|
|
151
|
+
- `test_excludes_lead_pane_even_if_title_matches`: caller pane title 을
|
|
152
|
+
`claude-worker` 로 강제해도 `$TMUX_PANE` 이므로 보존.
|
|
153
|
+
- `test_list_includes_agent_and_trace`: `--list` 가 registry trace pane + title-match
|
|
154
|
+
agent pane 을 합쳐 출력.
|
|
155
|
+
- 기존 케이스(미설정 no-op, registry 미존재 no-op, caller-scope 격리, unknown-option)는
|
|
156
|
+
회귀 없이 통과.
|
|
157
|
+
|
|
158
|
+
## enforcement 정직성 / 알려진 한계
|
|
159
|
+
|
|
160
|
+
- **선언적 동작**: phase 시작 자동정리는 lead 가 contract 를 따르는 선언이며 런타임 강제
|
|
161
|
+
훅이 없다(내부 phase 경계는 lead 만 안다). 기존 최종정리와 동일한 강제 수준이고,
|
|
162
|
+
`SessionEnd` 훅만 `/exit` 시점을 강제한다. 이 한계를 과장하지 않는다.
|
|
163
|
+
- **동시-run trade-off**: worker-agent pane 은 registry 가 없어 title 로만 식별한다.
|
|
164
|
+
같은 tmux 세션에서 **다른 okstra run 이 동시 실행** 중이면 그쪽 에이전트 pane 도
|
|
165
|
+
매칭될 수 있다(trace pane 의 caller-scoping 같은 격리 불가). 세션 범위로 좁혀 완화하되
|
|
166
|
+
완전 격리는 불가능 — 동일 세션 동시 okstra run 은 드문 케이스로 수용한다.
|
|
167
|
+
|
|
168
|
+
## 영향 / 전파
|
|
169
|
+
|
|
170
|
+
- 변경은 seed/source 파일(`scripts/`, `prompts/`, `templates/`)에 land → `npm run build`
|
|
171
|
+
→ `runtime/` → `okstra install` 로 최종 사용자 전파. 개인 `.claude/` 직접 수정 아님.
|
|
172
|
+
- 사용자 영향(CHANGES.md): "okstra-run 진행 중 새 phase 가 시작되면 이전 phase 의
|
|
173
|
+
worker/trace pane 이 자동으로 닫혀 화면에 좀비 pane 이 쌓이지 않는다."
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# requirements-discovery 도메인 fan-out 설계
|
|
2
|
+
|
|
3
|
+
> 작성: 2026-06-02 · 갱신: 2026-06-02 (packet 모델로 교정) · 상태: 설계 승인됨
|
|
4
|
+
|
|
5
|
+
## 1. 배경 / 문제
|
|
6
|
+
|
|
7
|
+
현재 `requirements-discovery` 는 **단일 작업 요청 → 단일 분류 → 단일 라우팅** 모델이다
|
|
8
|
+
([prompts/profiles/requirements-discovery.md](../../../prompts/profiles/requirements-discovery.md)).
|
|
9
|
+
하나의 요청 안에 버그 수정·개선·신규 구현이 **혼합**돼 있어도 한 덩어리로 흘러가므로,
|
|
10
|
+
다운스트림(error-analysis / implementation-planning / implementation)에서 성격이 다른
|
|
11
|
+
일이 섞인 채 진행된다.
|
|
12
|
+
|
|
13
|
+
목표: 혼합 요청을 **도메인별로 분리해 독립적인 작업 단위로 fan-out** 한다. 그래야
|
|
14
|
+
① 할 일이 도메인별로 분명히 구분되고 ② 착수 순서를 쉽게 정할 수 있으며 ③ 단위별로
|
|
15
|
+
독립 실행·추적이 된다.
|
|
16
|
+
|
|
17
|
+
## 2. 용어 / 경계 (중요 — 이전 초안의 혼동 교정)
|
|
18
|
+
|
|
19
|
+
- **packet = run 의 입력 파일.** `okstra-run` 은 `--task-brief <path>` 로 입력 파일을
|
|
20
|
+
받아 `instruction-set/task-brief.md` 로 복사해 phase 에 전달한다
|
|
21
|
+
([scripts/okstra_ctl/run.py:1143](../../../scripts/okstra_ctl/run.py),
|
|
22
|
+
[run.py:912](../../../scripts/okstra_ctl/run.py)). 이 파일은 **임의의 markdown** 이면
|
|
23
|
+
되고, `okstra-brief` 가 만들든 손으로 만들든 okstra-run 은 상관하지 않는다.
|
|
24
|
+
- **okstra-brief 는 이 설계와 무관하다.** 외부 소스 → packet 을 만들어 주는 *선택적·별개*
|
|
25
|
+
준비 도구일 뿐이다. fan-out 은 okstra-brief 를 **호출하지 않는다**.
|
|
26
|
+
- **fan-out 은 okstra-run 내부에서 완결되는 루프**다: requirements-discovery 가 packet 을
|
|
27
|
+
만들고, 그 packet 을 다시 okstra-run 이 `--task-brief` 로 먹는다.
|
|
28
|
+
|
|
29
|
+
> 이전 초안이 산출물을 `.okstra/briefs/` 의 "child brief" 로 둔 것은 okstra-brief 와
|
|
30
|
+
> 산출물·소유권이 겹치는 오류였다. 이번 설계의 산출물은 brief 가 아니라 **packet** 이고,
|
|
31
|
+
> 별도 템플릿·별도 저장 위치를 쓴다.
|
|
32
|
+
|
|
33
|
+
## 3. 핵심 결정 (brainstorming 합의)
|
|
34
|
+
|
|
35
|
+
| # | 결정 | 값 |
|
|
36
|
+
|---|---|---|
|
|
37
|
+
| 분할 지점 | lifecycle 어디서 | **requirements-discovery** (okstra-run 안에서 실행) |
|
|
38
|
+
| 도메인 축 | 무엇으로 나누나 | **기존 work-category 5-enum** (`bugfix / feature / refactor / ops / improvement`) |
|
|
39
|
+
| 산출물 | 각 단위가 내놓는 것 | **작업 항목당 packet 파일** (도메인 태그) → 새 task-key 로 fan-out |
|
|
40
|
+
| 입도 | packet 끊는 단위 | **작업 항목당 packet 1개** |
|
|
41
|
+
| 트리거 | 실행 주체 | **packet + 추천순서만 생성, 실행은 별도** (사람이 게이트, forbidden 규칙 준수) |
|
|
42
|
+
| 묶음/순서 | 의존·순서 표현 | **run 산출 영역의 fan-out 디렉토리 + index** |
|
|
43
|
+
| 다운스트림 다리 | packet → run | **`okstra-run --task-brief <packet 경로>`** (okstra-brief 미개입) |
|
|
44
|
+
|
|
45
|
+
## 4. 동작
|
|
46
|
+
|
|
47
|
+
### 4.1 트리거 & 분해 (requirements-discovery 내부)
|
|
48
|
+
|
|
49
|
+
분류 후 작업이 **다항목/다도메인인지** 판정한다.
|
|
50
|
+
|
|
51
|
+
- **단일 항목 / 단일 도메인** → 현행 그대로 (단일 라우팅, fan-out 없음). 기존 동작 100% 보존.
|
|
52
|
+
- **다항목 또는 도메인 혼합** → fan-out 분기.
|
|
53
|
+
|
|
54
|
+
판정 기준(휴리스틱, 고정 규칙 아님): 분류가 2개 이상 도메인에 걸치거나, 입력에서 독립 착수
|
|
55
|
+
가능한 작업 항목이 2개 이상 식별될 때. 경계 케이스는 "한 단위로 묶어도 무방한가?" 로
|
|
56
|
+
판단하고 근거를 리포트에 1줄 기록.
|
|
57
|
+
|
|
58
|
+
분해 산출: 각 작업 항목을 `domain`(5-enum) + scope + evidence(path:line) + `depends-on`
|
|
59
|
+
으로 정규화. **소스 편집·해법 설계·plan 작성은 여전히 non-goal** — 분해는 "무엇을 별도
|
|
60
|
+
단위로 볼지" 까지만. 각 단위의 해법은 다운스트림 몫.
|
|
61
|
+
|
|
62
|
+
### 4.2 산출물 (run 산출 영역)
|
|
63
|
+
|
|
64
|
+
run 산출물은 `.okstra/tasks/<task-group>/<task-id>/runs/<task-type>/` 아래 task-type 전용
|
|
65
|
+
서브디렉토리에 쌓인다 (선례: implementation 의 `carry/`, implementation-planning 의
|
|
66
|
+
`consumers.jsonl`).
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
.okstra/tasks/<task-group>/<task-id>/runs/requirements-discovery/
|
|
70
|
+
└── fan-out/
|
|
71
|
+
├── unit-001.md ← packet (별도 템플릿, domain 태그 + depends-on)
|
|
72
|
+
├── unit-002.md
|
|
73
|
+
└── index.md ← 단위 목록 + 추천 위상순서 (생성 뷰)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
packet 템플릿 — **신설** `templates/reports/fan-out-unit.template.md`. `brief.template.md`
|
|
77
|
+
와 **무관** (다른 템플릿·다른 홈·다른 소유자 → 중복/drift 없음). frontmatter 최소 필드:
|
|
78
|
+
|
|
79
|
+
- `domain: <bugfix|feature|refactor|ops|improvement>`
|
|
80
|
+
- `depends-on: []` — 같은 fan-out 내 다른 unit id 목록
|
|
81
|
+
- `recommended-next-phase: <error-analysis|implementation-planning>`
|
|
82
|
+
- packet 본문은 해당 작업 항목을 okstra-run 입력으로 쓸 수 있는 자족적 서술.
|
|
83
|
+
|
|
84
|
+
**SSOT 규칙 (drift 방지):**
|
|
85
|
+
|
|
86
|
+
- 각 packet 의 `domain` + `depends-on` 이 **권위 소스**.
|
|
87
|
+
- `index.md` 는 그로부터 만든 **생성 뷰** (단위를 추천 위상순서로 나열 + 1줄 요약).
|
|
88
|
+
"직접 편집 금지, packet 으로부터 재생성" 명시 → 두 번째 SSOT 아님.
|
|
89
|
+
- requirements-discovery 최종 리포트는 분해 결과를 본문에 중복하지 않고
|
|
90
|
+
"fan-out: N개 packet 생성 → `fan-out/index.md` 참조" **1줄 포인터**만 둔다.
|
|
91
|
+
|
|
92
|
+
### 4.3 순서 & 다운스트림
|
|
93
|
+
|
|
94
|
+
**fan-out 시점 (task 아직 없음):**
|
|
95
|
+
|
|
96
|
+
- `depends-on` 그래프로부터 **추천 착수 순서(위상정렬)** 를 계산해 `index.md` 에 기록.
|
|
97
|
+
- **순환(cycle) 검출**: `depends-on` 이 DAG 가 아니면 위상정렬 불가 → 인덱스 생성 시
|
|
98
|
+
순환을 감지해 리포트에 경고 + 해당 의존을 끊도록 표시.
|
|
99
|
+
|
|
100
|
+
**실행 (사람이 게이트, forbidden 규칙 준수):**
|
|
101
|
+
|
|
102
|
+
- 사용자가 각 packet 을 `okstra-run --task-brief <packet 경로>` 로 시작 → 각자 같은
|
|
103
|
+
task-group 아래 **새 task-key**.
|
|
104
|
+
- requirements-discovery 는 다운스트림 phase 를 자기 run 에서 시작하지 않는다.
|
|
105
|
+
- 추천 순서 준수는 사용자 재량 (인덱스는 권고).
|
|
106
|
+
|
|
107
|
+
**okstra-schedule 의 역할 (제약):**
|
|
108
|
+
|
|
109
|
+
- schedule 은 `task-manifest.json` 을 읽으므로 **단위가 task 로 시작된 *이후*** 에만 동작.
|
|
110
|
+
갓 만든 packet 은 task 가 아니라 schedule 이 직접 정렬하지 못한다.
|
|
111
|
+
- 역할 분담: fan-out 직후~착수 전엔 `index.md` 가 순서 가이드; 여러 단위가 task 가 된 뒤엔
|
|
112
|
+
okstra-schedule 이 task-manifest 기반 통합 일정 합성.
|
|
113
|
+
- **schedule 을 packet(pre-task)까지 읽도록 확장하는 것은 범위 제외 (YAGNI).**
|
|
114
|
+
|
|
115
|
+
## 5. 변경 표면 & enforcement
|
|
116
|
+
|
|
117
|
+
| 영역 | 파일 | 변경 |
|
|
118
|
+
|---|---|---|
|
|
119
|
+
| 계약 선언 | `prompts/profiles/requirements-discovery.md` | fan-out 분해 책임 추가(트리거/packet 발행/index). Non-goals 갱신: "작업 단위 분해" in-scope, 소스 편집·해법 설계·plan 작성은 여전히 non-goal |
|
|
120
|
+
| packet 템플릿 | `templates/reports/fan-out-unit.template.md` (신설) | `domain` + `depends-on` + `recommended-next-phase` frontmatter + 본문 가이드 |
|
|
121
|
+
| 5-enum SSOT | `scripts/okstra_ctl/work_categories.py` (신설) | 5-enum 상수 단일 정의(`improvement_lenses.py` 와 동일 위치 규약). validator 가 import. **기존 free-string `workCategory` 리팩터는 범위 제외** |
|
|
122
|
+
| enforcement | `validators/validate_fanout.py` (신설) + `validators/validate-run.py` 훅 | `validate-run.py` 가 `task_type == "requirements-discovery"` 이고 `fan-out/` 존재 시 자동 호출 (improvement-discovery 의 `validate_improvement_report.py` 패턴 그대로). 검사: packet `domain` ∈ 5-enum, `depends-on` 이 같은 fan-out 내 resolve, DAG 순환 없음, `index.md` 의 단위 목록 == 실제 packet, 추천순서가 유효 위상순서 |
|
|
123
|
+
| 순환검출 헬퍼 | `scripts/okstra_ctl/fanout.py` (신설) | DAG 위상정렬 + 순환검출. unit test 대상 |
|
|
124
|
+
| 문서 | `docs/kr/architecture.md`, `CHANGES.md` | 새 흐름 / 사용자 영향(`사용자 영향:` 1줄) |
|
|
125
|
+
|
|
126
|
+
> **스키마 변경 없음**: requirements-discovery 는 final-report 스키마에 전용 deliverable
|
|
127
|
+
> 블록이 없다(`followUpTasks` 의 phase-continuation 행만 요구). fan-out 포인터를 위해 새
|
|
128
|
+
> 스키마 구조를 만드는 건 과하다 — enforcement 는 `validate_fanout.py` 로 충분(YAGNI).
|
|
129
|
+
> 리포트의 1줄 포인터는 프로필이 지시하는 markdown 일 뿐 스키마 필드가 아니다.
|
|
130
|
+
|
|
131
|
+
**Rule 3 (선언 ≠ 강제) 매핑** — 프로필의 각 MUST 에 검증 1줄:
|
|
132
|
+
|
|
133
|
+
| 프로필 선언 | 강제 위치 |
|
|
134
|
+
|---|---|
|
|
135
|
+
| packet 은 `domain`(5-enum)·`depends-on`·`recommended-next-phase` 를 가진다 | validate_fanout.py frontmatter 검사 |
|
|
136
|
+
| `depends-on` 은 같은 fan-out 내 unit id 만 참조 | validate_fanout.py resolve 검사 |
|
|
137
|
+
| 의존 그래프는 DAG | validate_fanout.py 순환검출 헬퍼 |
|
|
138
|
+
| `index.md` 의 단위 목록 == 실제 packet | validate_fanout.py 정합성 검사 |
|
|
139
|
+
| 단일 요청은 fan-out 하지 않음 | (음성 케이스) e2e 시나리오로 회귀 확인 |
|
|
140
|
+
|
|
141
|
+
## 6. 테스트 전략
|
|
142
|
+
|
|
143
|
+
- **unit**: 순환검출/위상정렬 공유 헬퍼 · packet `domain`/`depends-on` 검증 · index 정합성.
|
|
144
|
+
- **e2e**: `tests-e2e/scenario-<id>-requirements-discovery-fanout.sh` — 혼합 입력 →
|
|
145
|
+
requirements-discovery → N packet + index 생성 → validate. 단일 입력 시 fan-out 하지
|
|
146
|
+
않음(회귀)도 같은 패밀리에서 확인.
|
|
147
|
+
|
|
148
|
+
## 7. 비목표 (이번 범위 아님)
|
|
149
|
+
|
|
150
|
+
- error-analysis 등 다른 phase 의 출력 분할 (필요 시 후속).
|
|
151
|
+
- 기존 free-string `workCategory` 사용처 전체를 5-enum SSOT 로 리팩터.
|
|
152
|
+
- okstra-schedule 을 packet(pre-task)까지 읽도록 확장.
|
|
153
|
+
- fan-out packet 의 자동 spawn (사람 게이트 유지).
|
|
154
|
+
- okstra-brief 변경 (이 설계와 무관).
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
## 1. 목적
|
|
13
13
|
|
|
14
|
-
`requirements-discovery`는 구현 전에 요청을 분류한다. bugfix, feature, improvement, refactor, ops
|
|
14
|
+
`requirements-discovery`는 구현 전에 요청을 분류한다. bugfix, feature, improvement, refactor, ops 중 무엇인지 판단하고, 다음 안전 phase가 `error-analysis`인지 `implementation-planning`인지 고른다. 직접 `implementation`으로 넘기는 것은 profile상 유효하지 않다. implementation은 승인된 `implementation-planning` report가 있어야 시작할 수 있다.
|
|
15
15
|
|
|
16
16
|
## 2. okstra-run wizard 흐름
|
|
17
17
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "okstra",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.40.0",
|
|
4
4
|
"description": "Multi-agent cross-verification orchestrator runtime + Claude Code skills.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "devonshin",
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
},
|
|
26
26
|
"scripts": {
|
|
27
27
|
"build": "node tools/build.mjs",
|
|
28
|
-
"prepack": "node tools/build.mjs"
|
|
28
|
+
"prepack": "node tools/build.mjs",
|
|
29
|
+
"test:js": "node --test tests-js/*.test.mjs"
|
|
29
30
|
}
|
|
30
31
|
}
|
package/runtime/BUILD.json
CHANGED
|
@@ -11,8 +11,9 @@ summary:
|
|
|
11
11
|
|
|
12
12
|
Skills, worker agents, and the codex wrapper are installed once per user under
|
|
13
13
|
~/.claude and ~/.okstra by scripts/okstra-install.sh. The user's settings.json
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
is never modified; okstra-ctl prepare provisions the project's
|
|
15
|
+
.claude/settings.local.json as a symlink to ~/.okstra/templates/settings.local.json,
|
|
16
|
+
which Claude Code auto-loads.
|
|
16
17
|
|
|
17
18
|
required arguments:
|
|
18
19
|
--project-id Globally unique project ID. Example: sample-project-v2-api.
|
|
@@ -36,9 +36,9 @@
|
|
|
36
36
|
# omitted or empty, no `--add-dir` is added (existing analysis-phase behavior).
|
|
37
37
|
#
|
|
38
38
|
# role is optional and used only to label the auto-spawned tmux trace pane
|
|
39
|
-
# (see "trace pane" section below). When omitted, it defaults to `
|
|
40
|
-
#
|
|
41
|
-
#
|
|
39
|
+
# (see "trace pane" section below). When omitted, it defaults to `worker`;
|
|
40
|
+
# the dispatching agent passes it explicitly (always `worker`) per the
|
|
41
|
+
# wrapper-invocation contract in agents/workers/_cli-wrapper-template.md.
|
|
42
42
|
#
|
|
43
43
|
# When role == `verifier`, the wrapper additionally grants the codex
|
|
44
44
|
# `workspace-write` sandbox write access to `~/.cargo` and `~/.rustup` (when
|
|
@@ -1,25 +1,31 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
#
|
|
3
|
-
# okstra-trace-cleanup.sh — manage tmux
|
|
4
|
-
#
|
|
5
|
-
# Claude Code session.
|
|
3
|
+
# okstra-trace-cleanup.sh — manage tmux panes created during an okstra run for
|
|
4
|
+
# the current Claude Code (lead) session.
|
|
6
5
|
#
|
|
7
|
-
# Two
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
6
|
+
# Two pane sources are cleaned for the lead's session:
|
|
7
|
+
# 1. Trace panes — `tail -F` siblings spawned by the codex/gemini wrappers
|
|
8
|
+
# (`okstra-codex-exec.sh`, `okstra-gemini-exec.sh`). Tracked in a registry
|
|
9
|
+
# keyed by the lead's `$TMUX_PANE`.
|
|
10
|
+
# 2. Worker-agent panes — panes the harness gives to dispatched okstra
|
|
11
|
+
# subagents (`claude-worker`, `codex-worker`, `gemini-worker`,
|
|
12
|
+
# `report-writer-worker`). Not registered anywhere by okstra; identified by
|
|
13
|
+
# a title allowlist within the lead's tmux session.
|
|
14
|
+
#
|
|
15
|
+
# The lead's own pane (`$TMUX_PANE`) is NEVER killed, even if its title matches
|
|
16
|
+
# the allowlist. The scan is scoped to the lead's session (`list-panes -s`),
|
|
17
|
+
# never the whole server (`-a`).
|
|
16
18
|
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
19
|
+
# Two modes:
|
|
20
|
+
# (default) kill every okstra pane (sources 1+2) and remove the registry
|
|
21
|
+
# file. Used by the `SessionEnd` hook, by the lead's per-phase
|
|
22
|
+
# auto-clean, and by the lead's end-of-run prompt (yes-branch).
|
|
23
|
+
# --list print one line per okstra pane (`<pane_id>\t<pane_title>`) so the
|
|
24
|
+
# lead can show the user what would be closed. Empty stdout when
|
|
25
|
+
# nothing is tracked.
|
|
20
26
|
#
|
|
21
|
-
# Failures are tolerated silently — a stale pane id, missing $TMUX, or a
|
|
22
|
-
#
|
|
27
|
+
# Failures are tolerated silently — a stale pane id, missing $TMUX, or a locked
|
|
28
|
+
# tmux client must never prevent Claude from exiting cleanly.
|
|
23
29
|
|
|
24
30
|
set -u
|
|
25
31
|
|
|
@@ -32,8 +38,9 @@ case "${1:-}" in
|
|
|
32
38
|
cat <<'USAGE'
|
|
33
39
|
usage: okstra-trace-cleanup.sh [--list]
|
|
34
40
|
|
|
35
|
-
(no args) kill every pane
|
|
36
|
-
|
|
41
|
+
(no args) kill every okstra pane for $TMUX_PANE (trace + worker-agent);
|
|
42
|
+
remove the trace registry file.
|
|
43
|
+
--list print "<pane_id>\t<pane_title>" per okstra pane; no kill.
|
|
37
44
|
--dry-run alias for --list.
|
|
38
45
|
USAGE
|
|
39
46
|
exit 0 ;;
|
|
@@ -51,25 +58,56 @@ registry_dir="${TMPDIR:-/tmp}/okstra-trace-panes"
|
|
|
51
58
|
safe_pane="${TMUX_PANE//[^A-Za-z0-9]/_}"
|
|
52
59
|
registry_file="$registry_dir/${safe_pane}.list"
|
|
53
60
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
61
|
+
# Collect okstra pane ids for the lead session: registered trace panes ∪
|
|
62
|
+
# title-allowlisted worker-agent panes, always excluding the lead pane itself.
|
|
63
|
+
collect_okstra_panes() {
|
|
64
|
+
local -a panes=()
|
|
65
|
+
local pid title
|
|
66
|
+
|
|
67
|
+
# (1) Registered trace panes — scoped to THIS lead's registry only, so
|
|
68
|
+
# concurrent Claude instances do not stomp each other's trace panes.
|
|
69
|
+
if [[ -f "$registry_file" ]]; then
|
|
70
|
+
while IFS= read -r pid; do
|
|
71
|
+
[[ -n "$pid" ]] || continue
|
|
72
|
+
[[ "$pid" == "$TMUX_PANE" ]] && continue
|
|
73
|
+
panes+=("$pid")
|
|
74
|
+
done < "$registry_file"
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
# (2) Title-allowlisted worker-agent panes in the lead's session.
|
|
78
|
+
# `list-panes -s -t <pane>` resolves the session containing that pane, so the
|
|
79
|
+
# scan never reaches other sessions (no `-a`).
|
|
80
|
+
while IFS=$'\t' read -r pid title; do
|
|
81
|
+
[[ -n "$pid" ]] || continue
|
|
82
|
+
[[ "$pid" == "$TMUX_PANE" ]] && continue
|
|
83
|
+
case "$title" in
|
|
84
|
+
*claude-worker*|*codex-worker*|*gemini-worker*|*report-writer-worker*)
|
|
85
|
+
panes+=("$pid") ;;
|
|
86
|
+
esac
|
|
87
|
+
done < <(tmux list-panes -s -t "$TMUX_PANE" \
|
|
88
|
+
-F '#{pane_id}'$'\t''#{pane_title}' 2>/dev/null || true)
|
|
89
|
+
|
|
90
|
+
# Dedupe — a live trace pane can match both the registry and the title scan.
|
|
91
|
+
if (( ${#panes[@]} )); then
|
|
92
|
+
printf '%s\n' "${panes[@]}" | awk 'NF && !seen[$0]++'
|
|
93
|
+
fi
|
|
94
|
+
}
|
|
57
95
|
|
|
58
96
|
if [[ "$MODE" == "list" ]]; then
|
|
59
97
|
while IFS= read -r pane_id; do
|
|
60
98
|
[[ -n "$pane_id" ]] || continue
|
|
61
|
-
# `display-message -p` resolves a *live* pane's title; for stale ids
|
|
62
|
-
#
|
|
99
|
+
# `display-message -p` resolves a *live* pane's title; for stale ids tmux
|
|
100
|
+
# exits non-zero — fall back to an empty title rather than failing.
|
|
63
101
|
title=$(tmux display-message -p -t "$pane_id" '#{pane_title}' 2>/dev/null || true)
|
|
64
102
|
printf '%s\t%s\n' "$pane_id" "$title"
|
|
65
|
-
done <
|
|
103
|
+
done < <(collect_okstra_panes)
|
|
66
104
|
exit 0
|
|
67
105
|
fi
|
|
68
106
|
|
|
69
107
|
while IFS= read -r pane_id; do
|
|
70
108
|
[[ -n "$pane_id" ]] || continue
|
|
71
109
|
tmux kill-pane -t "$pane_id" 2>/dev/null || true
|
|
72
|
-
done <
|
|
110
|
+
done < <(collect_okstra_panes)
|
|
73
111
|
|
|
74
112
|
rm -f "$registry_file" 2>/dev/null || true
|
|
75
113
|
exit 0
|