okstra 0.34.1 → 0.36.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.kr.md +27 -19
- package/README.md +27 -19
- package/docs/kr/architecture.md +59 -45
- package/docs/kr/cli.md +61 -18
- package/docs/pr-template-usage.md +65 -0
- package/docs/project-structure-overview.md +353 -354
- package/docs/superpowers/plans/2026-05-12-ticket-id-in-reports.md +1 -1
- package/docs/superpowers/plans/2026-05-14-convergence-queue-pruning.md +1 -1
- package/docs/superpowers/plans/2026-05-17-dual-format-final-report.md +1 -1
- package/docs/superpowers/plans/2026-05-20-final-report-language.md +1501 -0
- package/docs/superpowers/plans/2026-05-20-implementation-planning-multi-stage.md +1267 -0
- package/docs/superpowers/plans/2026-05-20-okstra-run-prompt-sot-b1.md +1007 -0
- package/docs/superpowers/plans/2026-05-20-wizard-messages-json-sot.md +720 -0
- package/docs/superpowers/plans/2026-05-20-wizard-prompt-json-sot-a1.md +681 -0
- package/docs/superpowers/plans/2026-05-21-improvement-discovery-task-type.md +1691 -0
- package/docs/superpowers/plans/2026-05-24-implementation-lead-context-slimming.md +1700 -0
- package/docs/superpowers/specs/2026-05-20-final-report-language-design.md +383 -0
- package/docs/superpowers/specs/2026-05-20-implementation-planning-multi-stage-design.md +320 -0
- package/docs/superpowers/specs/2026-05-20-okstra-run-prompt-sot-design.md +299 -0
- package/docs/superpowers/specs/2026-05-21-improvement-discovery-task-type-design.md +335 -0
- package/docs/task-process/README.md +74 -0
- package/docs/task-process/common-flow.md +166 -0
- package/docs/task-process/error-analysis.md +101 -0
- package/docs/task-process/final-verification.md +167 -0
- package/docs/task-process/implementation-planning.md +128 -0
- package/docs/task-process/implementation.md +149 -0
- package/docs/task-process/release-handoff.md +206 -0
- package/docs/task-process/requirements-discovery.md +115 -0
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/SKILL.md +30 -7
- package/runtime/agents/workers/claude-worker.md +31 -6
- package/runtime/agents/workers/codex-worker.md +37 -10
- package/runtime/agents/workers/gemini-worker.md +34 -7
- package/runtime/agents/workers/report-writer-worker.md +19 -10
- package/runtime/bin/okstra-central.sh +6 -6
- package/runtime/bin/okstra-codex-exec.sh +49 -28
- package/runtime/bin/okstra-gemini-exec.sh +39 -21
- package/runtime/bin/okstra-render-final-report.py +13 -2
- package/runtime/bin/okstra-wrapper-status.py +155 -0
- package/runtime/bin/okstra.sh +2 -2
- package/runtime/prompts/launch.template.md +1 -0
- package/runtime/prompts/profiles/_common-contract.md +11 -6
- package/runtime/prompts/profiles/_implementation-deliverable.md +53 -0
- package/runtime/prompts/profiles/_implementation-executor.md +60 -0
- package/runtime/prompts/profiles/_implementation-verifier.md +76 -0
- package/runtime/prompts/profiles/error-analysis.md +3 -7
- package/runtime/prompts/profiles/implementation-planning.md +22 -21
- package/runtime/prompts/profiles/implementation.md +28 -118
- package/runtime/prompts/profiles/improvement-discovery.md +42 -0
- package/runtime/prompts/profiles/release-handoff.md +1 -1
- package/runtime/prompts/profiles/requirements-discovery.md +8 -12
- package/runtime/prompts/wizard/prompts.ko.json +230 -0
- package/runtime/python/lib/okstra/cli.sh +2 -49
- package/runtime/python/lib/okstra/globals.sh +21 -21
- package/runtime/python/lib/okstra/interactive.sh +7 -7
- package/runtime/python/okstra_ctl/clarification_items.py +3 -9
- package/runtime/python/okstra_ctl/consumers.py +53 -0
- package/runtime/python/okstra_ctl/final_report_schema.py +0 -7
- package/runtime/python/okstra_ctl/i18n.py +73 -0
- package/runtime/python/okstra_ctl/improvement_lenses.py +44 -0
- package/runtime/python/okstra_ctl/index.py +1 -1
- package/runtime/python/okstra_ctl/paths.py +26 -20
- package/runtime/python/okstra_ctl/render.py +166 -207
- package/runtime/python/okstra_ctl/render_final_report.py +53 -10
- package/runtime/python/okstra_ctl/run.py +299 -108
- package/runtime/python/okstra_ctl/run_context.py +22 -0
- package/runtime/python/okstra_ctl/seeding.py +186 -0
- package/runtime/python/okstra_ctl/session.py +65 -7
- package/runtime/python/okstra_ctl/wizard.py +348 -127
- package/runtime/python/okstra_ctl/workflow.py +21 -2
- package/runtime/python/okstra_ctl/worktree.py +54 -1
- package/runtime/python/okstra_project/resolver.py +4 -3
- package/runtime/python/okstra_token_usage/report.py +2 -2
- package/runtime/schemas/final-report-v1.0.schema.json +22 -16
- package/runtime/skills/okstra-brief/SKILL.md +102 -218
- package/runtime/skills/okstra-convergence/SKILL.md +2 -3
- package/runtime/skills/okstra-inspect/SKILL.md +581 -0
- package/runtime/skills/okstra-report-writer/SKILL.md +35 -15
- package/runtime/skills/okstra-run/SKILL.md +8 -7
- package/runtime/skills/okstra-schedule/SKILL.md +14 -157
- package/runtime/skills/okstra-setup/SKILL.md +28 -1
- package/runtime/skills/okstra-team-contract/SKILL.md +16 -107
- package/runtime/templates/okstra.CLAUDE.md +104 -0
- package/runtime/templates/reports/brief.template.md +204 -0
- package/runtime/templates/reports/final-report.template.md +93 -98
- package/runtime/templates/reports/i18n/en.json +135 -0
- package/runtime/templates/reports/i18n/ko.json +135 -0
- package/runtime/templates/reports/implementation-planning-input.template.md +18 -0
- package/runtime/templates/reports/improvement-discovery-input.template.md +78 -0
- package/runtime/templates/reports/schedule.template.md +12 -3
- package/runtime/templates/reports/task-brief.template.md +2 -2
- package/runtime/templates/worker-prompt-preamble.md +108 -0
- package/runtime/validators/lib/fixtures.sh +30 -0
- package/runtime/validators/lib/runners.sh +1 -1
- package/runtime/validators/validate-implementation-plan-stages.py +211 -0
- package/runtime/validators/validate-run.py +121 -26
- package/runtime/validators/validate-workflow.sh +2 -2
- package/runtime/validators/validate_improvement_report.py +275 -0
- package/src/config.mjs +18 -0
- package/src/install.mjs +41 -14
- package/src/setup.mjs +133 -1
- package/src/uninstall.mjs +27 -3
- package/runtime/skills/okstra-history/SKILL.md +0 -165
- package/runtime/skills/okstra-logs/SKILL.md +0 -173
- package/runtime/skills/okstra-report-finder/SKILL.md +0 -111
- package/runtime/skills/okstra-status/SKILL.md +0 -246
- package/runtime/skills/okstra-time-summary/SKILL.md +0 -172
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
# implementation-planning multi-stage 산출 + sidecar carry-in — 설계
|
|
2
|
+
|
|
3
|
+
- 작성일: 2026-05-20
|
|
4
|
+
- 범위: `implementation-planning` task-type 의 산출 구조를 **항상 stage 단위**로 통일하고, 후속 `implementation` task 가 stage 별로 분할 실행되도록 한다. stage 간 carry-in 은 정적 명세(`Stage Exit Contract`) + 자동 sidecar evidence JSON 으로 전달한다.
|
|
5
|
+
- 비범위
|
|
6
|
+
- `requirements-discovery` / `error-analysis` / `final-verification` 의 산출 구조는 손대지 않는다.
|
|
7
|
+
- 신규 task-type 추가 없음 (`implementation-planning`, `implementation` 의 행동만 변경).
|
|
8
|
+
- **stage 단위의 병렬은 본 spec 의 범위 안** — 종속이 없는 stage 들은 두 개의 `implementation` run 이 동시에 잡아 같이 진행할 수 있다. 반면 **같은 stage 안 step 의 worker 동시 실행은 비범위** — step 의 "병렬 가능" 은 논리적 상호 독립을 의미할 뿐, 한 worker 가 step 1·2·3 을 동시에 돌리지는 않는다.
|
|
9
|
+
- 다국어/i18n.
|
|
10
|
+
|
|
11
|
+
## 1. 동기
|
|
12
|
+
|
|
13
|
+
현재 [`prompts/profiles/implementation-planning.md`](../../../prompts/profiles/implementation-planning.md) 은 final-report 안에 **평면 step 리스트** 하나를 산출하고, 작업이 크면 *"별도의 planning run 으로 나누라"* 는 prose 안내만 둔다 ([implementation-planning.md:89](../../../prompts/profiles/implementation-planning.md)). 결과적으로:
|
|
14
|
+
|
|
15
|
+
- 큰 작업이 한 plan 에 100+ step 으로 부풀어 [`prompts/profiles/implementation.md`](../../../prompts/profiles/implementation.md) executor 가 한 번에 다 끌고 가다 컨텍스트 한계·중간 실패에 약하다.
|
|
16
|
+
- 다음 단계로 넘어갈 때 carry 할 컨텍스트(이미 끝난 부분 / 남은 부분 / 실제로 생긴 식별자·커밋)가 흩어진다. 지금은 `--clarification-response <path>` 라는 단일 해명-전용 carry 통로만 있다.
|
|
17
|
+
- PR 도 한 덩어리로 묶이므로 코드 리뷰 단위가 비대해진다.
|
|
18
|
+
|
|
19
|
+
## 2. 핵심 원칙
|
|
20
|
+
|
|
21
|
+
### 2.1 분기 없는 단일 산출 구조
|
|
22
|
+
|
|
23
|
+
planner LLM 은 **항상** Stage Map + N 개의 Stage 섹션으로 산출한다. 작은 작업이면 `Stage 1` 한 개만, 큰 작업이면 N 개. "분할 활성화" 같은 분기 자체를 두지 않는다.
|
|
24
|
+
|
|
25
|
+
> 한 task-type, 한 산출 구조.
|
|
26
|
+
|
|
27
|
+
### 2.2 stage 안 = 병렬, stage 간 = depends-on 에만 순차
|
|
28
|
+
|
|
29
|
+
| 단위 | 성질 |
|
|
30
|
+
|---|---|
|
|
31
|
+
| 한 stage 안의 step 들 | 상호 독립 (논리 병렬) — 순서 자유. 같은 stage 안에서 step 1 을 먼저 하든 step 4 를 먼저 하든 결과 동일 |
|
|
32
|
+
| stage 사이 | `depends-on` 으로 명시된 stage 에 한해 순차. 명시되지 않은 stage 끼리는 **병렬 가능** (두 impl run 이 동시에 다른 stage 를 잡고 같이 진행) |
|
|
33
|
+
|
|
34
|
+
따라서 분할의 경계선은 **"종속이 생기는 지점"** 이고, 분할의 우선 방향은 §2.3 의 "병렬화 최대화" 다.
|
|
35
|
+
|
|
36
|
+
### 2.3 병렬화 최대화 우선 (분할 1 급 기준)
|
|
37
|
+
|
|
38
|
+
stage·step 을 구성할 때 **종속을 최소화해 병렬 가능 단위를 최대화하는 것을 1 순위 기준**으로 삼는다. 두 분할 안이 같은 step 수를 갖는다면, `depends-on` 링크가 더 적은(=병렬 가능 stage 가 더 많은) 쪽을 채택한다.
|
|
39
|
+
|
|
40
|
+
구체적 가이드:
|
|
41
|
+
- 한 stage 의 step 들은 서로 상호 독립이 되도록 묶는다. 상호 종속이 생기면 stage 를 분리한다.
|
|
42
|
+
- `depends-on (none)` 인 stage 가 가능한 한 여럿 나오도록 분할 — 첫 wave 에서 여러 impl run 이 동시에 출발할 수 있다.
|
|
43
|
+
- 종속이 진짜 필요한 곳에만 `depends-on` 을 적는다. "안전상 직렬화하자" 는 보수적 묶음은 금지 (병렬화 손실).
|
|
44
|
+
- 한 stage 의 exit contract 는 가능한 한 좁게 (하위 stage 가 의존할 표면 최소화).
|
|
45
|
+
|
|
46
|
+
### 2.3 stage 당 effective step ≤ 6
|
|
47
|
+
|
|
48
|
+
CLAUDE.md rule 14 의 함수 길이 cap 50 라인과 같은 정신. 6 을 넘으면 stage 를 늘려라.
|
|
49
|
+
|
|
50
|
+
"effective step" = blank line / 주석 / 순수 표 헤더 행 제외, 실제 행동 한 줄. 본 spec 의 validator (§5.4) 가 이 카운트를 강제한다.
|
|
51
|
+
|
|
52
|
+
### 2.4 carry-in 은 정적 명세 + 런타임 sidecar 의 합집합
|
|
53
|
+
|
|
54
|
+
| 종류 | 작성 주체 | 시점 | 형태 |
|
|
55
|
+
|---|---|---|---|
|
|
56
|
+
| 정적 명세 | planner LLM | plan 작성 시 | `Stage Exit Contract` 섹션 (예측 산출물) |
|
|
57
|
+
| 런타임 evidence | impl verifier | 해당 stage impl 완료 시 | `runs/<impl-key>/carry/stage-<N>.json` |
|
|
58
|
+
|
|
59
|
+
다음 stage 의 impl 은 두 가지 모두를 시작 시점에 자동 로드한다.
|
|
60
|
+
|
|
61
|
+
## 3. 데이터 모델
|
|
62
|
+
|
|
63
|
+
### 3.1 final-report.md (planner 산출)
|
|
64
|
+
|
|
65
|
+
기존의 `## 4.5 Stepwise Execution Order` 자리에 다음을 둔다.
|
|
66
|
+
|
|
67
|
+
```markdown
|
|
68
|
+
## 4.5 Stage Map
|
|
69
|
+
|
|
70
|
+
| stage | title | depends-on | step-count | exit-contract-summary |
|
|
71
|
+
|-------|-------|-----------|------------|------------------------|
|
|
72
|
+
| 1 | foo API 골격 | (none) | 4 | src/foo/api.ts:exportedFoo, db/migrations/2026-XX-foo.sql |
|
|
73
|
+
| 2 | baz settings 분리 | (none) | 2 | src/baz/settings.ts, env var BAZ_MODE |
|
|
74
|
+
| 3 | bar 통합 | 1, 2 | 3 | src/bar/use-foo.ts, GET /bar 엔드포인트 |
|
|
75
|
+
|
|
76
|
+
> Stage 1 과 Stage 2 는 `depends-on (none)` 이므로 두 개의 `implementation` run 이 동시에 잡아 병렬로 진행할 수 있다. Stage 3 는 둘 다 완료된 뒤 시작한다.
|
|
77
|
+
|
|
78
|
+
## 4.5.1 Stage 1: foo API 골격
|
|
79
|
+
|
|
80
|
+
### Carry-In
|
|
81
|
+
- (Stage 1: task-brief 의 요구사항만)
|
|
82
|
+
|
|
83
|
+
### Stepwise Execution Order (effective steps ≤ 6)
|
|
84
|
+
|
|
85
|
+
| step | ticketId | action | files | command | expected |
|
|
86
|
+
|------|----------|--------|-------|---------|----------|
|
|
87
|
+
| 1 | INV-1234 | … | … | … | … |
|
|
88
|
+
|
|
89
|
+
### Stage Exit Contract
|
|
90
|
+
- 추가/변경된 파일 (예측): src/foo/api.ts, db/migrations/…
|
|
91
|
+
- 새로 노출되는 식별자/타입/엔드포인트: `exportedFoo` 함수, …
|
|
92
|
+
- 다음 stage 가 사용 가능한 자원: …
|
|
93
|
+
|
|
94
|
+
### Stage Validation
|
|
95
|
+
- pre : `npm run lint`
|
|
96
|
+
- mid : `npm run test:unit`
|
|
97
|
+
- post : `npm run test:integration -- src/foo`
|
|
98
|
+
|
|
99
|
+
## 4.5.2 Stage 2: bar 통합
|
|
100
|
+
|
|
101
|
+
### Carry-In
|
|
102
|
+
- depends-on: Stage 1
|
|
103
|
+
- 정적 의존: Stage 1 exit contract 의 `exportedFoo`
|
|
104
|
+
- 런타임 의존: `runs/<impl-key>/carry/stage-1.json` (자동 로드)
|
|
105
|
+
|
|
106
|
+
### Stepwise Execution Order (effective steps ≤ 6)
|
|
107
|
+
…
|
|
108
|
+
### Stage Exit Contract
|
|
109
|
+
…
|
|
110
|
+
### Stage Validation
|
|
111
|
+
…
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
규칙:
|
|
115
|
+
- Stage 가 1 개여도 `## 4.5 Stage Map` 과 `## 4.5.1 Stage 1` 4 개 하위 섹션 모두 작성.
|
|
116
|
+
- `depends-on` 셀은 stage 번호 콤마 리스트 (`(none)` 또는 `1` 또는 `1,2`).
|
|
117
|
+
- 같은 stage 안 step 들은 종속 없음. 종속이 있으면 stage 를 분리하라는 신호.
|
|
118
|
+
|
|
119
|
+
### 3.2 sidecar evidence — `runs/<impl-key>/carry/stage-<N>.json`
|
|
120
|
+
|
|
121
|
+
`implementation` run 의 verifier 가 그 stage 완료 시 자동 작성.
|
|
122
|
+
|
|
123
|
+
```json
|
|
124
|
+
{
|
|
125
|
+
"schemaVersion": 1,
|
|
126
|
+
"sourcePlanPath": "runs/implementation-planning/<plan-key>/reports/final-report.md",
|
|
127
|
+
"stageNumber": 1,
|
|
128
|
+
"stageTitle": "foo API 골격",
|
|
129
|
+
"completedAt": "2026-05-20T13:42:18+09:00",
|
|
130
|
+
"stageCommitRange": { "base": "abc000", "head": "abc123" },
|
|
131
|
+
"filesChanged": ["src/foo/api.ts", "db/migrations/2026-XX-foo.sql"],
|
|
132
|
+
"newIdentifiers": ["exportedFoo"],
|
|
133
|
+
"stepResults": [
|
|
134
|
+
{ "step": 1, "status": "done", "commit": "abc111" },
|
|
135
|
+
{ "step": 2, "status": "done", "commit": "abc122" }
|
|
136
|
+
],
|
|
137
|
+
"validationsPassed": ["lint", "test:unit", "test:integration"],
|
|
138
|
+
"notes": []
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
- 위치: `runs/<impl-key>/carry/stage-<N>.json` (impl run 의 task-root 하위).
|
|
143
|
+
- 작성 시점: 그 stage 의 모든 step + Stage Validation post 명령이 모두 통과한 시점.
|
|
144
|
+
- 누가: `agents/workers/<verifier>.md` 의 final 단계가 책임진다. Lead 는 그 출력을 받아 파일에 쓴다.
|
|
145
|
+
- 멱등: 같은 stage 의 sidecar 가 이미 있으면 덮어쓰기 전에 stop (이미 완료된 stage 를 재실행하려는 시도이므로 명시적으로 `--force-stage` 같은 향후 인수 필요 — 본 spec 의 비범위).
|
|
146
|
+
|
|
147
|
+
### 3.3 plan↔impl reverse link — `runs/<plan-key>/consumers.jsonl`
|
|
148
|
+
|
|
149
|
+
impl prepare 시점과 종료 시점에 한 줄씩 append. 멱등.
|
|
150
|
+
|
|
151
|
+
```json
|
|
152
|
+
{"impl_task_key": "INV-1234-impl-2026-05-20T13", "stage": 1, "status": "started", "started_at": "2026-05-20T13:00:00+09:00", "head_commit": "abc000"}
|
|
153
|
+
{"impl_task_key": "INV-1234-impl-2026-05-20T13", "stage": 1, "status": "done", "completed_at": "2026-05-20T13:42:18+09:00", "carry_path": "runs/.../carry/stage-1.json"}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
- 잠금: `~/.okstra/.locks/<plan-task-key>.consumers.lock` 으로 직렬화 (한 plan 을 다수 impl 이 동시에 가리킬 수 있음).
|
|
157
|
+
- 멱등: `(impl_task_key, stage, status)` 가 이미 있으면 append 생략.
|
|
158
|
+
- 읽기: planner LLM, `okstra-status`, `okstra-history` 가 이 파일로 plan 의 소비 현황을 본다.
|
|
159
|
+
|
|
160
|
+
## 4. 흐름
|
|
161
|
+
|
|
162
|
+
```
|
|
163
|
+
plan run ──► final-report.md (Stage Map + N stages, APPROVED 1 회)
|
|
164
|
+
│
|
|
165
|
+
│ implementation prepare 시 validator §5.4 실행
|
|
166
|
+
▼
|
|
167
|
+
impl run #1 ──► auto stage 선택 → Stage 1
|
|
168
|
+
├─ consumers.jsonl ← {stage:1, started}
|
|
169
|
+
├─ stage 1 commits ──► PR #1 ("Stage 1: foo API 골격")
|
|
170
|
+
└─ carry/stage-1.json
|
|
171
|
+
│
|
|
172
|
+
└─ consumers.jsonl ← {stage:1, done}
|
|
173
|
+
▼
|
|
174
|
+
impl run #2 ──► auto stage 선택 → Stage 2 (앞 stage sidecar 자동 흡수)
|
|
175
|
+
└─ … → PR #2
|
|
176
|
+
▼
|
|
177
|
+
모든 stage done → release-handoff (PR #1, #2, … 일람을 release PR 본문에)
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## 5. 변경 대상 파일 (seed)
|
|
181
|
+
|
|
182
|
+
CLAUDE.md memory rule (`feedback_okstra_fixes_target_end_users`) 에 따라 모든 변경은 seed/template 파일에 가한다.
|
|
183
|
+
|
|
184
|
+
### 5.1 [`prompts/profiles/implementation-planning.md`](../../../prompts/profiles/implementation-planning.md)
|
|
185
|
+
|
|
186
|
+
- "큰 작업이면 별도 planning run 으로 나누라"는 안내(line 89 부근) 제거.
|
|
187
|
+
- 대체: §2 의 5 원칙을 작성 지침으로 옮긴다.
|
|
188
|
+
1. 항상 Stage Map + N 개의 Stage 섹션 산출.
|
|
189
|
+
2. stage 안 step 은 상호 독립 (논리 병렬). stage 사이는 `depends-on` 으로 명시한 곳만 순차이고, 명시하지 않은 stage 끼리는 병렬 가능.
|
|
190
|
+
3. **병렬화 최대화 — `depends-on` 링크를 최소화하라.** `depends-on (none)` 인 stage 가 가능한 한 여럿 나오도록 분할. 안전상의 보수적 직렬화 금지. 두 분할 안이 같은 step 총수라면 의존 링크가 더 적은 쪽 채택.
|
|
191
|
+
4. stage 당 effective step ≤ 6, 초과 시 stage 분리. 한 stage 의 exit contract 는 가능한 한 좁게.
|
|
192
|
+
5. 각 stage 에 `Carry-In` / `Stepwise Execution Order` / `Stage Exit Contract` / `Stage Validation` 4 개 하위 섹션 작성. `depends-on (none)` 인 stage 의 `Carry-In` 은 task-brief 만 인용 가능. depends-on 이 있는 stage 는 그 stage 들의 정적 exit contract 와 (있다면) sidecar 경로를 명시.
|
|
193
|
+
- self-check 절차 추가:
|
|
194
|
+
- stage 별 effective step 수를 직접 세어 6 이하인지 확인.
|
|
195
|
+
- `depends-on` 그래프에 사이클이 없는지 확인.
|
|
196
|
+
- "이 종속을 끊을 수 있는가?" 를 모든 `depends-on` 항목마다 자문 — 끊을 수 있으면 끊어 병렬 stage 로 분리.
|
|
197
|
+
|
|
198
|
+
### 5.2 [`templates/reports/implementation-planning-input.template.md`](../../../templates/reports/implementation-planning-input.template.md)
|
|
199
|
+
|
|
200
|
+
기존 평면 step 예시 자리에 두 종 예시:
|
|
201
|
+
- 1-stage 예시 (작은 작업)
|
|
202
|
+
- 다중-stage 예시 (스타일 통일)
|
|
203
|
+
|
|
204
|
+
### 5.3 [`prompts/profiles/implementation.md`](../../../prompts/profiles/implementation.md)
|
|
205
|
+
|
|
206
|
+
- "approved plan 에서 추출" 절차([implementation.md:69](../../../prompts/profiles/implementation.md)) 를 갱신:
|
|
207
|
+
- Stage Map 을 파싱한다.
|
|
208
|
+
- 시작 stage 결정: `--stage` 인수가 있으면 그 stage, 없으면 auto = **"`depends-on` 의 모든 stage 가 `consumers.jsonl` 에 `status:done` 으로 기록돼 있고, 본인은 아직 done 이 아닌 stage 중 가장 빠른 번호"**. 그런 stage 가 여러 개일 수 있으며, 그 경우 **다른 impl run 이 동시에 이웃 stage 를 잡아도 둘 다 즉시 진행 가능** (consumers.jsonl 의 lock 은 직렬 append 만 보장하고 stage 자체의 동시 실행은 막지 않음).
|
|
209
|
+
- 앞 stage (= 본 stage 의 `depends-on` 에 있는 stage) 들의 `carry/stage-<i>.json` 을 모두 로드해 컨텍스트에 주입. `depends-on (none)` 이면 sidecar 로드 없이 task-brief 만 사용.
|
|
210
|
+
- 종료 절차에 추가:
|
|
211
|
+
- 해당 stage 의 `carry/stage-<N>.json` 작성.
|
|
212
|
+
- `consumers.jsonl` 두 줄(`started`, `done`) 갱신.
|
|
213
|
+
- stage 별 PR 생성 (제목 `Stage <N>: <title>`, 본문에 carry-in 요약 + 다음 stage 안내).
|
|
214
|
+
|
|
215
|
+
### 5.4 신규 validator — `validators/implementation_plan_stages.py`
|
|
216
|
+
|
|
217
|
+
검사 항목:
|
|
218
|
+
|
|
219
|
+
| 검사 | 내용 |
|
|
220
|
+
|------|------|
|
|
221
|
+
| S1 | `## 4.5 Stage Map` 섹션이 존재한다. |
|
|
222
|
+
| S2 | Stage Map 표의 행이 stage 번호 1 부터 단조 증가한다. |
|
|
223
|
+
| S3 | 각 stage 가 `## 4.5.<i> Stage <i>:` 섹션을 갖는다. |
|
|
224
|
+
| S4 | 각 stage 섹션이 `Carry-In` / `Stepwise Execution Order` / `Stage Exit Contract` / `Stage Validation` 4 개 하위 섹션 모두 갖는다. |
|
|
225
|
+
| S5 | 각 stage 의 `Stepwise Execution Order` 표의 effective step 수가 6 이하 (blank/주석/표 헤더 제외). |
|
|
226
|
+
| S6 | Stage Map 의 `depends-on` 항목이 존재하는 stage 번호 또는 `(none)` 이다. |
|
|
227
|
+
| S7 | Stage Map 의 `step-count` 셀이 실제 `Stepwise Execution Order` 표의 effective step 수와 일치한다. |
|
|
228
|
+
| S8 | Stage Map 의 `depends-on` 그래프에 사이클이 없다 (DAG). 자기 자신 참조 금지. |
|
|
229
|
+
|
|
230
|
+
실행 시점: [`scripts/okstra_ctl/run.py`](../../../scripts/okstra_ctl/run.py) 의 `prepare_task_bundle` 에서 `task_type == "implementation"` 분기 (현재 `APPROVED_PLAN_PATTERN` 검사가 이루어지는 자리 부근, 약 line 514+). 위반 시 `PrepareError` 로 stage 번호 + 위반 항목 코드(S1~S7) 와 함께 중단.
|
|
231
|
+
|
|
232
|
+
이중 강제 (CLAUDE.md additional rule 3 "declaration vs enforcement"):
|
|
233
|
+
- 선언: §5.1 의 self-check 5 줄.
|
|
234
|
+
- 강제: 본 validator. 두 군데가 같은 규칙을 본다.
|
|
235
|
+
|
|
236
|
+
### 5.5 [`scripts/okstra_ctl/run.py`](../../../scripts/okstra_ctl/run.py)
|
|
237
|
+
|
|
238
|
+
- 새 입력 인수: `--stage <N|auto>` (기본 `auto`). `implementation` task-type 에만 의미. 다른 task-type 에 주어지면 `PrepareError`.
|
|
239
|
+
- approved plan 파싱: §5.4 의 validator 호출 후, Stage Map 을 ctx 에 `parsed_stage_map` 으로 추가. 한 stage 메타의 필드는 5 개: `stage_number: int`, `title: str`, `depends_on: List[int]`, `step_count: int`, `exit_contract_summary: str`. impl worker prompt 가 그걸로 시작 stage 를 결정한다.
|
|
240
|
+
- `consumers.jsonl` append 로직 (lock 사용).
|
|
241
|
+
- 평면 step 리스트 fallback 경로는 제거 (분기 없음 원칙).
|
|
242
|
+
|
|
243
|
+
### 5.6 [`scripts/okstra_ctl/wizard.py`](../../../scripts/okstra_ctl/wizard.py)
|
|
244
|
+
|
|
245
|
+
`task_type == "implementation"` 서브플로우에 새 step `stage_pick` 추가:
|
|
246
|
+
|
|
247
|
+
- approved plan 파일을 읽어 Stage Map 의 stage 들을 picker option 으로 동적 생성.
|
|
248
|
+
- option label 형태: `auto (다음 미완료 stage)`, `1: foo API 골격`, `2: bar 통합`, …
|
|
249
|
+
- 기본값: `auto`.
|
|
250
|
+
- 사용자가 stage 를 선택하면 wizard state 에 `selected_stage` 를 기록, 최종 `render-args` 출력에 `--stage <value>` 로 매핑.
|
|
251
|
+
- approved plan 파싱 실패 시 (포맷 위반) `ok: false` 로 validator 와 같은 코드(S1~S7) 를 에러 메시지에 노출 → 사용자가 plan 을 고치고 다시 시도.
|
|
252
|
+
|
|
253
|
+
> wizard 의 `_build_*` 함수가 한글 prompt 를 코드 하드코딩하는 현 패턴을 그대로 따른다 (별도 spec [`2026-05-20-okstra-run-prompt-sot-design.md`](2026-05-20-okstra-run-prompt-sot-design.md) Phase A1 에서 본격적으로 분리 예정).
|
|
254
|
+
|
|
255
|
+
### 5.7 [`agents/workers/`](../../../agents/workers/) — worker prompt 의 검증 단계
|
|
256
|
+
|
|
257
|
+
`implementation` task 의 각 worker prompt (claude / codex / gemini 등) 의 마지막 검증 단계에:
|
|
258
|
+
- "해당 stage 의 모든 step + Stage Validation post 명령이 통과한 시점에 §3.2 의 schema 로 evidence JSON 을 출력하라" 명시.
|
|
259
|
+
- Lead 가 worker 의 출력을 받아 `runs/<impl-key>/carry/stage-<N>.json` 으로 저장하는 책임은 lead prompt (impl profile §5.3) 에 명시.
|
|
260
|
+
|
|
261
|
+
> writing-plans 단계에서 실제 `agents/workers/` 의 어느 파일에 어느 절을 추가할지 grep 으로 정확한 절 경계를 잡는다.
|
|
262
|
+
|
|
263
|
+
### 5.8 stage 별 PR 분리
|
|
264
|
+
|
|
265
|
+
[`prompts/profiles/implementation.md`](../../../prompts/profiles/implementation.md) 의 PR 생성 절차에:
|
|
266
|
+
|
|
267
|
+
- PR 제목: `Stage <N>: <title>`
|
|
268
|
+
- PR 본문 frontmatter:
|
|
269
|
+
- `## Stage` — 번호 + 제목
|
|
270
|
+
- `## Carry-In summary` — Stage Map 의 `depends-on` + 앞 sidecar 의 핵심 식별자/커밋 SHA
|
|
271
|
+
- `## Next stage` — 다음 stage 번호·제목·예상 carry (없으면 `(last stage)`)
|
|
272
|
+
- 기존 `templates/reports/settings.template.json` 또는 PR 본문 템플릿이 있다면 새 필드 추가.
|
|
273
|
+
|
|
274
|
+
### 5.9 (선택) [`prompts/profiles/release-handoff.md`](../../../prompts/profiles/release-handoff.md)
|
|
275
|
+
|
|
276
|
+
release PR 본문에 plan run 의 `consumers.jsonl` 에서 stage PR 목록을 읽어 일람으로 포함. 첫 cut 에서는 사용자가 PR URL 들을 직접 입력하게 두고, 자동 수집은 후속 spec 으로 분리할 수 있다.
|
|
277
|
+
|
|
278
|
+
> 미해결 (작음): release-handoff 가 stage PR 들을 자동 수집할지, 사용자가 PR URL 들을 직접 입력할지. writing-plans 단계에서 한 번 더 결정.
|
|
279
|
+
|
|
280
|
+
## 6. wizard 흐름 변경 (요약)
|
|
281
|
+
|
|
282
|
+
```
|
|
283
|
+
…(기존)
|
|
284
|
+
base-ref pick + git rev-parse 검증 (implementation 도 동일)
|
|
285
|
+
──► approved-plan path (APPROVED 마커 검사) — 기존
|
|
286
|
+
──► stage_pick (신규) : auto / 1 / 2 / ...
|
|
287
|
+
──► executor pick — 기존
|
|
288
|
+
──► Use defaults / Customize — 기존
|
|
289
|
+
──► confirm — 기존
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
stage_pick 의 option label 에 stage title 이 포함되므로, 사용자는 어느 stage 를 실행할지 picker 안에서 한 줄 요약으로 바로 확인 가능.
|
|
293
|
+
|
|
294
|
+
## 7. 호환성
|
|
295
|
+
|
|
296
|
+
okstra 는 pre-1.0 (CLAUDE.md memory `feedback_pre_v1_no_compat`). 기존 평면 step 리스트로 작성된 final-report 는 deprecation 알림 없이 validator (§5.4) 가 즉시 차단한다. 사용자는 plan 을 새 구조로 다시 쓰면 된다.
|
|
297
|
+
|
|
298
|
+
## 8. 미해결 / 후속 spec 으로 분리
|
|
299
|
+
|
|
300
|
+
본 spec 에서 결정하지 않고 별도로 다룰 사항:
|
|
301
|
+
|
|
302
|
+
1. release-handoff 가 stage PR 목록을 **자동 수집**할지, 사용자 입력으로 받을지 (§5.9).
|
|
303
|
+
2. 이미 완료된 stage 의 재실행 (`--force-stage`) UX.
|
|
304
|
+
3. stage 사이에서 plan 자체를 수정해야 할 때의 절차 — 현재는 "approved 마커가 1 회뿐"이라 plan 수정 시 모든 stage 에 대해 다시 승인을 받아야 하는데, 이 흐름을 더 매끄럽게 만들 여지가 있다.
|
|
305
|
+
|
|
306
|
+
## 9. 검증 시나리오 (수동 QA 용)
|
|
307
|
+
|
|
308
|
+
writing-plans 단계에서 자동 테스트 plan 으로 옮기기 전, 다음 시나리오를 손으로 돌려본다.
|
|
309
|
+
|
|
310
|
+
| # | 시나리오 | 기대 결과 |
|
|
311
|
+
|---|----------|-----------|
|
|
312
|
+
| Q1 | 1-stage plan 으로 `implementation` 실행 | stage 1 자동 선택, sidecar 1 개, PR 1 개 |
|
|
313
|
+
| Q2 | 3-stage plan 의 `implementation` 첫 번째 실행 | stage 1 자동, stage 2/3 의 sidecar 없음 |
|
|
314
|
+
| Q3 | Q2 의 stage 1 완료 후 두 번째 `implementation` 실행 | stage 2 자동, stage-1.json 컨텍스트로 주입됨 |
|
|
315
|
+
| Q4 | step 7 개짜리 stage 가 있는 plan 의 `implementation` prepare | validator S5 위반 에러로 중단 |
|
|
316
|
+
| Q5 | `depends-on: 9` (없는 stage) 인 plan | validator S6 위반 에러 |
|
|
317
|
+
| Q6 | Q3 에서 사용자가 `--stage 1` 강제 지정 | sidecar 존재로 인해 명시적 에러 (재실행은 §8.2 의 비범위) |
|
|
318
|
+
| Q7 | 두 stage 가 모두 `depends-on (none)` 인 plan 에서 두 impl run 동시 시작 | 두 run 다 즉시 진행. 한 쪽이 `auto` 면 가장 빠른 미점유 번호 자동 선택. consumers.jsonl 두 줄 모두 append (lock). |
|
|
319
|
+
| Q8 | stage 3 의 `depends-on: 1, 2` 인 plan 에서 stage 2 만 done, stage 1 아직 진행 중일 때 `auto` 호출 | stage 3 는 시작 불가. stage 1 의 done 을 기다리거나, stage 1 자체를 잡아야 함. validator 가 아닌 런타임이 막음. |
|
|
320
|
+
| Q9 | depends-on 사이클 (stage 1→2, 2→1) 이 있는 plan | validator S8 위반 에러로 prepare 단계에서 중단 |
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
# okstra-run prompt 작성 로직 SOT 단일화 — 설계
|
|
2
|
+
|
|
3
|
+
- 작성일: 2026-05-20
|
|
4
|
+
- 범위: okstra-run 흐름에서 prompt 가 작성되는 두 surface 의 단일 출처(SOT) 확립
|
|
5
|
+
- **Phase B1**: Lead 실행 prompt (`claude-execution-prompt.md`) 의 토큰 — 우선 진행
|
|
6
|
+
- **Phase A1**: Wizard 사용자 질문 (한글 label) — B1 머지 후 별도 PR
|
|
7
|
+
- 비범위: i18n 활성화, prompt 카피 자체의 문구 개선, worker dispatch prompt(런타임 외부), profile 파일(이미 파일 SOT)
|
|
8
|
+
|
|
9
|
+
## 1. 동기
|
|
10
|
+
|
|
11
|
+
okstra-run 의 prompt 작성 로직은 두 곳에서 **이중 정의** 형태로 운영되고 있다.
|
|
12
|
+
|
|
13
|
+
### 1.1 Lead 실행 prompt — 토큰의 이중 정의
|
|
14
|
+
|
|
15
|
+
- 템플릿: [`prompts/launch.template.md`](../../../prompts/launch.template.md) — `{{TOKEN}}` 33개
|
|
16
|
+
- 렌더러: [`scripts/okstra_ctl/render.py:1469-1627`](../../../scripts/okstra_ctl/render.py) 의 `mapping = {...}` — 100여 개 `"{{TOKEN}}" → ctx.get("CTX_KEY", "")` 매핑
|
|
17
|
+
|
|
18
|
+
새 토큰 추가 시 두 파일을 모두 수정해야 하며, 한쪽이 빠지면 silent drift (템플릿에 미치환 `{{X}}` 잔존, 또는 dead mapping entry) 가 발생한다. 또한 토큰명과 ctx 키가 일부는 같고(`{{PROJECT_ROOT}}` ↔ `ctx["PROJECT_ROOT"]`) 일부는 임의로 다르다 (`{{TASK_MANIFEST_PATH}}` ↔ `ctx["TASK_MANIFEST_FILE"]`).
|
|
19
|
+
|
|
20
|
+
### 1.2 Wizard prompt — 코드 안의 한글 하드코딩
|
|
21
|
+
|
|
22
|
+
- [`scripts/okstra_ctl/wizard.py`](../../../scripts/okstra_ctl/wizard.py) 의 `_build_*` 함수 ~30개에 `Prompt(label="한글 질문")` 가 산재. 일부는 f-string 보간 (`f"기존 brief 경로 [{state.existing_brief_path}] 를 유지할까요?"`).
|
|
23
|
+
- 결과: prompt 문구 한 줄 수정도 Python 코드 변경이며, 리뷰 diff 에서 카피 변경의 시그널이 묻힌다. i18n 도 불가.
|
|
24
|
+
|
|
25
|
+
### 1.3 원칙
|
|
26
|
+
|
|
27
|
+
> "한 토큰/한 prompt 문구는 한 군데서만 정의된다."
|
|
28
|
+
|
|
29
|
+
- Phase B1: 템플릿이 단독 권위. 렌더러는 hand-maintained mapping dict 없이 `{{TOKEN}}` 을 `ctx[TOKEN]` 로 1:1 lookup.
|
|
30
|
+
- Phase A1: yaml 파일이 단독 권위. wizard.py 의 `_build_*` 함수는 step id 로 텍스트를 lookup 하고 state 보간만 수행.
|
|
31
|
+
- 양쪽 모두 누락 시 silent drift 가 아니라 **fail-fast**.
|
|
32
|
+
|
|
33
|
+
## 2. Phase B1 — Lead prompt token SOT
|
|
34
|
+
|
|
35
|
+
### 2.1 토큰 카테고리 (현 mapping dict 분석)
|
|
36
|
+
|
|
37
|
+
| 카테고리 | 개수 | 예시 | 처리 |
|
|
38
|
+
|---|---|---|---|
|
|
39
|
+
| C1. token == ctx 키 | ~55 | `{{PROJECT_ROOT}}` ↔ `ctx["PROJECT_ROOT"]` | 그대로 통과 |
|
|
40
|
+
| C2. token ≠ ctx 키 (rename 필요) | ~30 | `{{TASK_MANIFEST_PATH}}` ↔ `ctx["TASK_MANIFEST_FILE"]`, `{{LEAD_MODEL}}` ↔ `ctx["LEAD_MODEL_DISPLAY"]` | **ctx producer 측 키 이름을 token 명에 맞춤** |
|
|
41
|
+
| C3. computed (ctx 키 없음) | ~9 | `{{TEAM_CREATION_GATE}}`, `{{TEAM_ROLE_LINES}}`, `{{EXECUTION_STATUS_TABLE_ROWS}}` | **별도 함수 `inject_lead_prompt_computed_tokens(ctx)` 로 분리** → ctx 에 미리 머지 |
|
|
42
|
+
| C4. default fallback | 4 | `{{VALIDATION_STATUS}}` 기본 `"not-run"`, `{{RELATED_TASKS_BULLETS}}` 기본 `"- None recorded"`, `{{AVAILABLE_MCP_SERVERS}}` 기본 `build_available_mcp_servers_block(...)` | **ctx 생성 단계에서 미리 채움** (`ctx.setdefault(...)`) |
|
|
43
|
+
| frontmatter overlay | (별도 dict) | `_frontmatter_mapping(fm_ctx)` | 그대로 유지 — frontmatter 는 별도 SOT 로 이미 분리됨 |
|
|
44
|
+
|
|
45
|
+
### 2.2 ctx 키 rename 표 (C2)
|
|
46
|
+
|
|
47
|
+
| 옛 ctx 키 | 새 ctx 키 (= token 명) |
|
|
48
|
+
|---|---|
|
|
49
|
+
| `TASK_MANIFEST_FILE` | `TASK_MANIFEST_PATH` |
|
|
50
|
+
| `TASK_INDEX_FILE` | `TASK_INDEX_PATH` |
|
|
51
|
+
| `INSTRUCTION_SET_DIR` | `INSTRUCTION_SET_PATH` |
|
|
52
|
+
| `RUN_MANIFEST_FILE` | `RUN_MANIFEST_PATH` |
|
|
53
|
+
| `TIMELINE_FILE` | `TIMELINE_PATH` |
|
|
54
|
+
| `FINAL_REPORT_FILE` | `FINAL_REPORT_PATH` |
|
|
55
|
+
| `FINAL_STATUS_FILE` | `FINAL_STATUS_PATH` |
|
|
56
|
+
| `TEAM_STATE_FILE` | `TEAM_STATE_PATH` |
|
|
57
|
+
| `WORKER_RESULTS_DIR` | `WORKER_RESULTS_PATH` |
|
|
58
|
+
| `RUN_VALIDATOR_SCRIPT` | `RUN_VALIDATOR_PATH` |
|
|
59
|
+
| `RUN_ERRORS_LOG_FILE` | `RUN_ERRORS_LOG_PATH` |
|
|
60
|
+
| `<CLAUDE\|CODEX\|GEMINI\|REPORT_WRITER>_WORKER_ERRORS_SIDECAR_FILE` | `…_WORKER_ERRORS_SIDECAR_PATH` |
|
|
61
|
+
| `LEAD_MODEL_DISPLAY` | `LEAD_MODEL` |
|
|
62
|
+
| `CLAUDE_WORKER_MODEL_DISPLAY` | `CLAUDE_WORKER_MODEL` |
|
|
63
|
+
| `CODEX_WORKER_MODEL_DISPLAY` | `CODEX_WORKER_MODEL` |
|
|
64
|
+
| `GEMINI_WORKER_MODEL_DISPLAY` | `GEMINI_WORKER_MODEL` |
|
|
65
|
+
| `REPORT_WRITER_MODEL_DISPLAY` | `REPORT_WRITER_MODEL` |
|
|
66
|
+
| `FINAL_REPORT_TEMPLATE_FILE` | `FINAL_REPORT_TEMPLATE_PATH` |
|
|
67
|
+
| `CLARIFICATION_RESPONSE_FILE` | `CLARIFICATION_RESPONSE_PATH` |
|
|
68
|
+
| `CLAUDE_RESUME_COMMAND_FILE` | `CLAUDE_RESUME_COMMAND_PATH` |
|
|
69
|
+
| `ANALYSIS_TYPE` (ctx) | `TASK_TYPE` (ctx 키 + 토큰명 통일) — 현 mapping 이 토큰 `{{TASK_TYPE}}` 와 `{{ANALYSIS_TYPE}}` 양쪽 모두 같은 ctx 키 `ANALYSIS_TYPE` 로부터 채움. 토큰 alias `{{ANALYSIS_TYPE}}` 는 템플릿에서 사용 시 함께 제거 (사용처 grep 으로 확인) |
|
|
70
|
+
| `REVIEW_PROFILE` (ctx) | `ANALYSIS_PROFILE` (토큰명에 맞춤) |
|
|
71
|
+
| `SELECTED_REVIEWERS` (ctx) | `RECOMMENDED_ANALYSERS` (토큰명에 맞춤) |
|
|
72
|
+
|
|
73
|
+
규칙: `*_FILE` / `*_DIR` / `*_SCRIPT` 접미사를 `*_PATH` 로 통일, `*_DISPLAY` 접미사 제거, legacy alias 제거.
|
|
74
|
+
|
|
75
|
+
### 2.3 데이터 흐름
|
|
76
|
+
|
|
77
|
+
**현재**
|
|
78
|
+
```
|
|
79
|
+
ctx (run.py 에서 채워짐, 키 이름 자유)
|
|
80
|
+
│
|
|
81
|
+
▼
|
|
82
|
+
render_template_file(template, out, ctx)
|
|
83
|
+
├─ 거대한 mapping dict {{TOKEN}}→ctx.get(...) 직접 정의
|
|
84
|
+
├─ + computed lines (team_creation_gate_block 등) 함수 내부에서 생성
|
|
85
|
+
├─ replace 루프
|
|
86
|
+
├─ phase-block strip
|
|
87
|
+
├─ frontmatter overlay
|
|
88
|
+
└─ write
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**B1 이후**
|
|
92
|
+
```
|
|
93
|
+
ctx 빌드 (run.py) ← C2 rename 반영 + C4 default 채움
|
|
94
|
+
│
|
|
95
|
+
▼
|
|
96
|
+
inject_lead_prompt_computed_tokens(ctx) ← C3 (team_creation_gate 등 ctx 에 머지)
|
|
97
|
+
│
|
|
98
|
+
▼
|
|
99
|
+
render_template_with_ctx(template, out, ctx)
|
|
100
|
+
├─ tokens = re.findall(r"\{\{([A-Z][A-Z0-9_]*)\}\}", template)
|
|
101
|
+
├─ for t in tokens: rendered = rendered.replace("{{"+t+"}}", str(ctx[t]))
|
|
102
|
+
│ ↑ 미존재 키는 KeyError → RenderError("undefined lead-prompt token: {{X}}")
|
|
103
|
+
├─ phase-block strip
|
|
104
|
+
├─ frontmatter overlay (그대로)
|
|
105
|
+
└─ write
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### 2.4 변경 파일
|
|
109
|
+
|
|
110
|
+
1. **`scripts/okstra_ctl/render.py`**
|
|
111
|
+
- 기존 `render_template_file` 의 `mapping = {...}` 블록 제거.
|
|
112
|
+
- 신규 `render_template_with_ctx(template_path, output_path, ctx) -> None` — 위 동작.
|
|
113
|
+
- 신규 `inject_lead_prompt_computed_tokens(ctx: dict) -> None` — 기존 함수 내부의 compute 블록 (team_creation_gate_block / worker_result_lines / team_role_lines / model_assignment_lines / execution_status / sentence 들 / AVAILABLE_MCP_SERVERS 기본) 을 분리해 ctx in-place 갱신.
|
|
114
|
+
- 옛 `render_template_file` 은 즉시 삭제 (pre-1.0 — no shim). CLI dispatcher (`render.py:1684`) 의 `template` subcommand 도 새 함수로 갱신.
|
|
115
|
+
|
|
116
|
+
2. **`scripts/okstra_ctl/run.py`** + ctx producer 들 (`paths.py`, `run_context.py`, `models.py`, `render.py` 내 다른 render_* 함수)
|
|
117
|
+
- 2.2 표대로 ctx 키 rename.
|
|
118
|
+
- `render_template_file(prompt_template, ...)` 호출을 `render_template_with_ctx(...)` 로 교체. 호출 직전에 `inject_lead_prompt_computed_tokens(ctx)` 호출 추가.
|
|
119
|
+
- C4 default 값은 ctx 빌드 함수 안에서 미리 채움.
|
|
120
|
+
|
|
121
|
+
3. **`prompts/launch.template.md`** — 변경 없음 (이미 SOT 위치). rename 으로 `{{TASK_TYPE}}` 등 토큰 명이 ctx 키와 정합화될 뿐.
|
|
122
|
+
|
|
123
|
+
### 2.5 검증
|
|
124
|
+
|
|
125
|
+
| 종류 | 위치 | 목적 |
|
|
126
|
+
|---|---|---|
|
|
127
|
+
| Unit (신규) | `tests/test_render_template_with_ctx.py` | (a) 모든 토큰 치환 시 미치환 잔여 없음 (b) ctx 누락 시 `RenderError` 발생 (c) phase-block strip + frontmatter overlay 동작 유지 |
|
|
128
|
+
| Unit (신규) | `tests/test_lead_prompt_token_resolution.py` | 합성 ctx (모든 필수 키 포함) + 실제 `prompts/launch.template.md` 로 dry-run 렌더 → 정규식 `\{\{[A-Z_]+\}\}` 잔여 0 검증. **CI 게이트** |
|
|
129
|
+
| Unit (회귀) | 기존 `tests/test_run.py`, `tests/test_render.py` 등 | ctx 키 rename 영향 회귀 — 기존 단위 테스트가 옛 키 사용 시 함께 업데이트 |
|
|
130
|
+
| E2E (게이트 한 줄 추가) | `tests-e2e/scenario-01-record-start-reconcile.sh` 등 | 실제 task bundle 생성 + `claude-execution-prompt.md` 에 `{{...}}` 잔여 없음 grep |
|
|
131
|
+
|
|
132
|
+
### 2.6 마이그레이션 순서 (단일 PR — 원자적)
|
|
133
|
+
|
|
134
|
+
이 변경은 ctx 키 rename 이 핵심이라 한 PR 에 묶임. 반쪽 머지 시 caller 가 옛 키로 ctx 쓰면 즉시 깨짐.
|
|
135
|
+
|
|
136
|
+
1. `inject_lead_prompt_computed_tokens(ctx)` 추가 — 기존 `render_template_file` 안의 compute 블록 (C3 + C4 default) 을 그대로 떼서 ctx 에 머지. mapping dict 는 임시 유지.
|
|
137
|
+
2. `render_template_with_ctx(template, out, ctx)` 추가. caller 아직 없음.
|
|
138
|
+
3. `run.py:778` 의 `render_template_file(prompt_template, ...)` 호출을 새 함수로 교체. 직전에 `inject_lead_prompt_computed_tokens(ctx)` 호출 추가. `final_report_template` 호출은 그 템플릿이 `{{TOKEN}}` 미사용이므로 영향 없음.
|
|
139
|
+
4. ctx producer 측 키 rename (2.2 표). grep 게이트:
|
|
140
|
+
```bash
|
|
141
|
+
! grep -rn 'TASK_MANIFEST_FILE\|TASK_INDEX_FILE\|INSTRUCTION_SET_DIR\|RUN_MANIFEST_FILE\|TIMELINE_FILE\|FINAL_REPORT_FILE\|FINAL_STATUS_FILE\|TEAM_STATE_FILE\|WORKER_RESULTS_DIR\|RUN_VALIDATOR_SCRIPT\|RUN_ERRORS_LOG_FILE\|LEAD_MODEL_DISPLAY\|CLAUDE_WORKER_MODEL_DISPLAY\|CODEX_WORKER_MODEL_DISPLAY\|GEMINI_WORKER_MODEL_DISPLAY\|REPORT_WRITER_MODEL_DISPLAY\|FINAL_REPORT_TEMPLATE_FILE\|CLARIFICATION_RESPONSE_FILE\|CLAUDE_RESUME_COMMAND_FILE\|REVIEW_PROFILE\|SELECTED_REVIEWERS\|ANALYSIS_TYPE' scripts/
|
|
142
|
+
```
|
|
143
|
+
5. 옛 `render_template_file` 삭제 + CLI `template` subcommand 를 새 함수로.
|
|
144
|
+
6. 신규 unit 테스트 + e2e grep 게이트 추가. `python3 -m pytest tests/` 통과, `bash tests-e2e/scenario-01-record-start-reconcile.sh` 통과.
|
|
145
|
+
7. `npm run build` 로 `runtime/` 갱신 검증 (커밋은 prepack 이 처리).
|
|
146
|
+
8. `CHANGES.md` 항목 추가:
|
|
147
|
+
> **Lead prompt 토큰 SOT 단일화** — `claude-execution-prompt.md` 의 `{{TOKEN}}` 들이 `prompts/launch.template.md` 단독 권위. `render.py` 의 이중 정의(mapping dict) 제거. 사용자 영향: 없음 (산출물 동일).
|
|
148
|
+
|
|
149
|
+
### 2.7 실패모드
|
|
150
|
+
|
|
151
|
+
| 증상 | 원인 | 대응 |
|
|
152
|
+
|---|---|---|
|
|
153
|
+
| `RenderError: undefined lead-prompt token: {{X}}` | 새 토큰을 템플릿에 넣었지만 ctx 빌드 코드를 안 만짐 | 메시지가 짚는 지점에 ctx[X] 채우는 코드 추가 |
|
|
154
|
+
| 출력에 미치환 `{{X}}` 잔존 | 정규식 mismatch (예: 소문자 토큰) | 정적 테스트가 catch — 정규식 패턴 또는 토큰 표기 교정 |
|
|
155
|
+
| ctx 키 rename 누락 caller | 옛 키로 ctx 쓰는 코드 잔존 | grep + 테스트 — 렌더 단계에서 토큰 lookup 실패로 fail-fast |
|
|
156
|
+
|
|
157
|
+
### 2.8 롤백
|
|
158
|
+
|
|
159
|
+
PR 단위로 revert. ctx 키 rename 이 묶여 있어 revert 도 원자적. 데이터 마이그레이션 없음 — 디스크 산출물은 동일.
|
|
160
|
+
|
|
161
|
+
## 3. Phase A1 — Wizard prompt YAML SOT (B1 머지 후 별도 PR)
|
|
162
|
+
|
|
163
|
+
### 3.1 외부화 대상
|
|
164
|
+
|
|
165
|
+
| 종류 | 예시 | 외부화? |
|
|
166
|
+
|---|---|---|
|
|
167
|
+
| `label` (고정) | `"어느 task?"` | ✅ yaml |
|
|
168
|
+
| `label` (state 보간) | `f"기존 brief 경로 [{state.existing_brief_path}] 를 유지할까요?"` | ✅ yaml — `{existing_brief_path}` placeholder + `.format(**view)` |
|
|
169
|
+
| `echo_template` | `"task: {value}"` | ✅ yaml |
|
|
170
|
+
| 정적 option `label` | `Option(value="_NEW_", label="Start a brand-new task")` | ✅ yaml |
|
|
171
|
+
| 동적 option `label` (state-derived) | `f"{key} · {phase} · next: {nxt}"` | ❌ 코드 잔류 — 데이터 포매팅 |
|
|
172
|
+
| `kind`, `multi`, `step` 상수 | `S_TASK_PICK` 등 | ❌ 코드 잔류 — 상태 머신 구조 |
|
|
173
|
+
| `options[]` 구성 자체 (값/순서) | task_pick 의 catalog 매핑 | ❌ 코드 잔류 — state 가 정함 |
|
|
174
|
+
|
|
175
|
+
### 3.2 YAML 스키마 (`prompts/wizard/prompts.ko.yaml`)
|
|
176
|
+
|
|
177
|
+
```yaml
|
|
178
|
+
schema_version: 1
|
|
179
|
+
locale: ko
|
|
180
|
+
|
|
181
|
+
steps:
|
|
182
|
+
task_pick:
|
|
183
|
+
label: "어느 task?"
|
|
184
|
+
echo_template: "task: {value}"
|
|
185
|
+
options:
|
|
186
|
+
_NEW_: "Start a brand-new task"
|
|
187
|
+
|
|
188
|
+
task_group:
|
|
189
|
+
label: "Task group 을 알려주세요 (예: backend-api, INV-1234, refactor)"
|
|
190
|
+
echo_template: "task-group: {value}"
|
|
191
|
+
|
|
192
|
+
task_group_with_suggestion:
|
|
193
|
+
label: "Task group? (brief 추천: {suggestion})"
|
|
194
|
+
echo_template: "task-group: {value}"
|
|
195
|
+
|
|
196
|
+
brief_keep:
|
|
197
|
+
label: "기존 brief 경로 [{existing_brief_path}] 를 유지할까요?"
|
|
198
|
+
echo_template: "brief: {value}"
|
|
199
|
+
options:
|
|
200
|
+
"yes": "유지"
|
|
201
|
+
"no": "변경"
|
|
202
|
+
|
|
203
|
+
# ... 모든 step 동일 패턴
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
**placeholder 규약** (yaml ↔ 코드 contract):
|
|
207
|
+
- `{value}` — user answer (echo_template 에만 사용)
|
|
208
|
+
- 그 외 placeholder — `_p(step_id, **kwargs)` 호출 시 해당 키워드로 채움
|
|
209
|
+
- 미정의 placeholder → `WizardError("placeholder '<x>' missing for step '<id>'")` 로 fail-fast
|
|
210
|
+
|
|
211
|
+
### 3.3 로더 (wizard.py)
|
|
212
|
+
|
|
213
|
+
```python
|
|
214
|
+
import yaml
|
|
215
|
+
_PROMPTS = yaml.safe_load(
|
|
216
|
+
(Path(__file__).resolve().parents[1] / "prompts" / "wizard" / "prompts.ko.yaml")
|
|
217
|
+
.read_text(encoding="utf-8")
|
|
218
|
+
)["steps"]
|
|
219
|
+
|
|
220
|
+
def _p(step_id: str, **vars: str) -> dict:
|
|
221
|
+
raw = _PROMPTS[step_id] # 미정의 → 즉시 KeyError → WizardError 로 wrap
|
|
222
|
+
try:
|
|
223
|
+
label = raw["label"].format(**vars) if vars else raw["label"]
|
|
224
|
+
except KeyError as exc:
|
|
225
|
+
raise WizardError(f"placeholder {exc} missing for step '{step_id}'") from exc
|
|
226
|
+
return {
|
|
227
|
+
"label": label,
|
|
228
|
+
"echo_template": raw["echo_template"],
|
|
229
|
+
"options": raw.get("options", {}),
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
`_build_brief_keep` 변경 예:
|
|
234
|
+
```python
|
|
235
|
+
def _build_brief_keep(state: WizardState) -> Prompt:
|
|
236
|
+
t = _p("brief_keep", existing_brief_path=state.existing_brief_path)
|
|
237
|
+
return Prompt(
|
|
238
|
+
step=S_BRIEF_KEEP, kind="pick",
|
|
239
|
+
label=t["label"],
|
|
240
|
+
options=[_opt(k, v) for k, v in t["options"].items()],
|
|
241
|
+
echo_template=t["echo_template"],
|
|
242
|
+
)
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### 3.4 빌드/검증
|
|
246
|
+
|
|
247
|
+
| 시점 | 검증 항목 | 위치 |
|
|
248
|
+
|---|---|---|
|
|
249
|
+
| 모듈 임포트 시 | yaml `schema_version == 1`, `steps` 가 dict | wizard.py `_load_prompts()` |
|
|
250
|
+
| `_p()` 호출 시 | step_id 존재, placeholder 부족 시 즉시 에러 | `_p()` 본문 |
|
|
251
|
+
| 신규 unit test | `tests/test_wizard_prompts.py` — wizard.py 안의 모든 step 상수 (`S_*`) 가 yaml 키에 존재. 역도 성립 (yaml 잉여키 0) | 신규 |
|
|
252
|
+
| 빌드 | `tools/build.mjs` 의 sync 대상에 `prompts/` 가 이미 포함 (`runtime/prompts/wizard/prompts.ko.yaml` 로 복사됨) | 변경 없음 |
|
|
253
|
+
|
|
254
|
+
### 3.5 마이그레이션 순서 (B1 머지 후 별도 PR)
|
|
255
|
+
|
|
256
|
+
1. `prompts/wizard/prompts.ko.yaml` 생성 — wizard.py 의 `_build_*` 함수 28개에서 한글 문자열을 모두 추출해 단일 yaml 로 정리. step_id 는 기존 `S_*` 상수의 소문자 형태로.
|
|
257
|
+
2. wizard.py 에 `_PROMPTS` / `_p()` 로더 추가.
|
|
258
|
+
3. `_build_*` 함수 한 번에 모두 yaml lookup 형태로 변경 — 부분 마이그레이션 시 SOT 가 깨지므로 원자적.
|
|
259
|
+
4. 신규 unit test 추가 + 기존 wizard 테스트 회귀 확인.
|
|
260
|
+
5. 한글 문자열이 wizard.py 에 0개 남았는지 grep 게이트:
|
|
261
|
+
```bash
|
|
262
|
+
! grep -nP '[가-힣]' scripts/okstra_ctl/wizard.py
|
|
263
|
+
```
|
|
264
|
+
6. `CHANGES.md` 항목 추가 (사용자 영향: 없음 — UI 동일).
|
|
265
|
+
|
|
266
|
+
### 3.6 의도적으로 안 하는 것
|
|
267
|
+
|
|
268
|
+
- **i18n 활성화** — 본 PR 은 SOT 확립만. `prompts.en.yaml` 추가나 locale 선택 메커니즘은 별도 작업.
|
|
269
|
+
- **모든 prompt 외부화** — 동적 option label (state-derived) 은 코드 잔류. yaml 은 "UI 카피" 만 담당.
|
|
270
|
+
- **runtime hot-reload** — 모듈 임포트 시 1회 로드. 변경 시 재시작 필요.
|
|
271
|
+
|
|
272
|
+
### 3.7 실패모드
|
|
273
|
+
|
|
274
|
+
| 증상 | 원인 | 대응 |
|
|
275
|
+
|---|---|---|
|
|
276
|
+
| `KeyError: 'brief_keep'` | yaml 에 step 누락 | 빌드 시 grep + unit test 가 catch |
|
|
277
|
+
| `WizardError: placeholder 'existing_brief_path' missing for step 'brief_keep'` | yaml label 이 코드 호출보다 더 많은 placeholder 요구 | 메시지가 짚는 step 의 `_p()` 호출에 인자 추가 |
|
|
278
|
+
| 한글 문자열 회귀 | wizard.py 에 한글 추가됨 | 위 grep 게이트가 CI 에서 fail |
|
|
279
|
+
|
|
280
|
+
## 4. 두 phase 사이의 관계
|
|
281
|
+
|
|
282
|
+
- 독립적 — B1 가 A1 의 전제는 아니지만, ctx/token 정합화 (B1) 가 먼저 잡혀야 wizard 가 다루는 surface 와 lead prompt 가 다루는 surface 의 분리가 더 명확해진다.
|
|
283
|
+
- 한 design 문서로 묶는 이유: 둘 다 "okstra-run prompt 작성 로직 SOT 단일화" 라는 단일 동기에서 출발하며, 사용자(devonshin)의 결정 단위가 동일.
|
|
284
|
+
- 구현은 분리 (B1 → 머지 → 별도 PR 로 A1).
|
|
285
|
+
|
|
286
|
+
## 5. 결정 사항 요약
|
|
287
|
+
|
|
288
|
+
| 결정 | 선택지 | 채택 | 이유 |
|
|
289
|
+
|---|---|---|---|
|
|
290
|
+
| Lead prompt SOT 방식 | B1 convention-based / B2 token registry yaml / B3 Jinja2 전환 | **B1** | 가장 작은 변경으로 진짜 SOT. B3 는 phase-block strip 등 부수 로직 재구성이 함께 와서 위험. |
|
|
291
|
+
| Wizard prompt SOT 방식 | A1 YAML / A2 per-step .md / A3 Python 상수 | **A1** | 비-코더 편집·i18n 친화. 한 파일이라 검색·diff 비용 낮음. |
|
|
292
|
+
| 호환 처리 | shim 유지 / 즉시 제거 | **즉시 제거** | pre-1.0 (CLAUDE.md memory: feedback_pre_v1_no_compat) |
|
|
293
|
+
| PR 분할 | 한 PR / 두 PR | **두 PR (B1 → A1)** | B1 의 ctx 키 rename 이 원자적이라야 함. A1 은 wizard 만 변경이라 독립 머지 가능. |
|
|
294
|
+
|
|
295
|
+
## 6. 후속 작업 (out-of-scope)
|
|
296
|
+
|
|
297
|
+
- i18n 활성화: `prompts.en.yaml` 추가 + locale 선택 메커니즘
|
|
298
|
+
- Wizard 동적 option label 의 i18n: 데이터-카피 분리 (현재 코드 잔류)
|
|
299
|
+
- Lead prompt 의 동적 블록 로직 (team_role_lines 등) 을 Jinja2 로 이전 (B3 옵션 — 향후 별도 design)
|