okstra 0.25.1 → 0.27.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 (48) hide show
  1. package/README.kr.md +16 -0
  2. package/README.md +16 -0
  3. package/docs/kr/architecture.md +3 -7
  4. package/docs/kr/cli.md +47 -4
  5. package/docs/kr/performance-improvement-plan-v2.md +23 -0
  6. package/docs/kr/performance-improvement-plan.md +22 -0
  7. package/docs/superpowers/specs/2026-05-15-implementation-plan-verification-design.md +254 -0
  8. package/package.json +1 -1
  9. package/runtime/BUILD.json +2 -2
  10. package/runtime/agents/SKILL.md +30 -2
  11. package/runtime/bin/okstra.sh +1 -1
  12. package/runtime/prompts/profiles/_common-contract.md +30 -1
  13. package/runtime/prompts/profiles/error-analysis.md +12 -0
  14. package/runtime/prompts/profiles/implementation-planning.md +23 -0
  15. package/runtime/prompts/profiles/requirements-discovery.md +20 -0
  16. package/runtime/python/lib/okstra/cli.sh +8 -7
  17. package/runtime/python/lib/okstra/globals.sh +3 -1
  18. package/runtime/python/lib/okstra/usage.sh +8 -4
  19. package/runtime/python/okstra_ctl/render.py +35 -0
  20. package/runtime/python/okstra_ctl/run.py +27 -6
  21. package/runtime/python/okstra_ctl/run_context.py +1 -1
  22. package/runtime/python/okstra_ctl/wizard.py +259 -10
  23. package/runtime/python/okstra_token_usage/blocks.py +5 -1
  24. package/runtime/python/okstra_token_usage/claude.py +16 -1
  25. package/runtime/python/okstra_token_usage/collect.py +17 -3
  26. package/runtime/python/okstra_token_usage/pricing.py +159 -24
  27. package/runtime/skills/okstra-brief/SKILL.md +532 -65
  28. package/runtime/skills/okstra-context-loader/SKILL.md +25 -11
  29. package/runtime/skills/okstra-convergence/SKILL.md +235 -8
  30. package/runtime/skills/okstra-history/SKILL.md +68 -37
  31. package/runtime/skills/okstra-logs/SKILL.md +26 -4
  32. package/runtime/skills/okstra-report-finder/SKILL.md +49 -22
  33. package/runtime/skills/okstra-report-writer/SKILL.md +59 -64
  34. package/runtime/skills/okstra-run/SKILL.md +53 -39
  35. package/runtime/skills/okstra-schedule/SKILL.md +51 -20
  36. package/runtime/skills/okstra-setup/SKILL.md +31 -12
  37. package/runtime/skills/okstra-status/SKILL.md +20 -8
  38. package/runtime/skills/okstra-team-contract/SKILL.md +27 -15
  39. package/runtime/skills/okstra-time-summary/SKILL.md +53 -16
  40. package/runtime/templates/reports/final-report.template.md +34 -0
  41. package/runtime/templates/reports/settings.template.json +7 -4
  42. package/runtime/validators/lib/fixtures.sh +10 -2
  43. package/runtime/validators/lib/validate-assets.sh +50 -24
  44. package/runtime/validators/validate-brief.py +385 -0
  45. package/runtime/validators/validate-brief.sh +35 -0
  46. package/runtime/validators/validate-run.py +71 -0
  47. package/runtime/validators/validate-workflow.sh +7 -33
  48. package/src/wizard.mjs +21 -5
@@ -36,6 +36,9 @@ multi-MB logs; analysis-phase dispatches are typically smaller.
36
36
 
37
37
  ## Step 0: Verify okstra runtime + project setup
38
38
 
39
+ Before any other step, ensure both the okstra runtime and the current
40
+ project's okstra metadata are in place:
41
+
39
42
  ```bash
40
43
  if command -v okstra >/dev/null 2>&1; then
41
44
  OKSTRA_CMD="okstra"
@@ -46,6 +49,8 @@ $OKSTRA_CMD ensure-installed >/dev/null 2>&1 || {
46
49
  echo "FAIL: okstra not installed; tell the user to run: npx okstra@latest install" >&2
47
50
  exit 1
48
51
  }
52
+ eval "$($OKSTRA_CMD paths --shell)"
53
+ export PYTHONPATH="$OKSTRA_PYTHONPATH"
49
54
  OKSTRA_PROJECT_INFO="$($OKSTRA_CMD check-project --json)" || {
50
55
  echo "FAIL: this project has no okstra setup. Tell the user to run /okstra-setup first." >&2
51
56
  echo "$OKSTRA_PROJECT_INFO" >&2
@@ -68,7 +73,7 @@ LOGS_ROOT="$PROJECT_ROOT/.project-docs/okstra/tasks"
68
73
  # columns: size_bytes | mtime_epoch | path
69
74
  find "$LOGS_ROOT" -type f -path '*/runs/*/prompts/*.log' \
70
75
  -printf '%s\t%T@\t%p\n' 2>/dev/null \
71
- | sort -k2,2nr
76
+ | sort -k1,1nr
72
77
  ```
73
78
 
74
79
  On macOS, `find -printf` is unavailable. Fall back to `stat`:
@@ -78,7 +83,7 @@ find "$LOGS_ROOT" -type f -path '*/runs/*/prompts/*.log' 2>/dev/null \
78
83
  | while IFS= read -r p; do
79
84
  stat -f '%z%t%m%t%N' "$p"
80
85
  done \
81
- | sort -k2,2nr
86
+ | sort -k1,1nr
82
87
  ```
83
88
 
84
89
  If the result is empty, report `No wrapper log files found under <PROJECT_ROOT>` and exit.
@@ -117,27 +122,40 @@ Total: N files, X.X MB across M tasks under <PROJECT_ROOT>
117
122
  ## Step 3: Suggested cleanup commands
118
123
 
119
124
  Emit a fenced bash block the user can copy-paste. Do NOT execute these.
125
+ Each block pairs a dry-run preview (`-print`) with the destructive
126
+ (`-delete`) command so the user can confirm the match set before
127
+ committing.
120
128
 
121
129
  ```markdown
122
130
  ## Cleanup options (manual)
123
131
 
124
132
  # 7일 이상 된 로그만 삭제
133
+ find <PROJECT_ROOT>/.project-docs/okstra/tasks \
134
+ -type f -path '*/runs/*/prompts/*.log' -mtime +7 -print # dry-run
125
135
  find <PROJECT_ROOT>/.project-docs/okstra/tasks \
126
136
  -type f -path '*/runs/*/prompts/*.log' -mtime +7 -delete
127
137
 
128
138
  # 30일 이상 된 로그만 삭제
139
+ find <PROJECT_ROOT>/.project-docs/okstra/tasks \
140
+ -type f -path '*/runs/*/prompts/*.log' -mtime +30 -print # dry-run
129
141
  find <PROJECT_ROOT>/.project-docs/okstra/tasks \
130
142
  -type f -path '*/runs/*/prompts/*.log' -mtime +30 -delete
131
143
 
132
144
  # 특정 task-group 의 로그 일괄 삭제 (예: dev-9388)
145
+ find <PROJECT_ROOT>/.project-docs/okstra/tasks/dev-9388 \
146
+ -type f -name '*.log' -print # dry-run
133
147
  find <PROJECT_ROOT>/.project-docs/okstra/tasks/dev-9388 \
134
148
  -type f -name '*.log' -delete
135
149
 
136
150
  # 특정 task-id 의 로그 일괄 삭제 (예: dev-9428)
151
+ find <PROJECT_ROOT>/.project-docs/okstra/tasks/*/dev-9428 \
152
+ -type f -name '*.log' -print # dry-run
137
153
  find <PROJECT_ROOT>/.project-docs/okstra/tasks/*/dev-9428 \
138
154
  -type f -name '*.log' -delete
139
155
 
140
156
  # 전체 일괄 삭제 (주의)
157
+ find <PROJECT_ROOT>/.project-docs/okstra/tasks \
158
+ -type f -path '*/runs/*/prompts/*.log' -print # dry-run
141
159
  find <PROJECT_ROOT>/.project-docs/okstra/tasks \
142
160
  -type f -path '*/runs/*/prompts/*.log' -delete
143
161
  ```
@@ -152,10 +170,14 @@ End the response with these short reminders:
152
170
  - Logs are truncated on each re-dispatch of the same `seq`, so deleting an
153
171
  in-flight run's log will cause the wrapper to recreate an empty file on
154
172
  the next dispatch — no data loss beyond the current trace.
173
+ - **If a dispatch is currently running, check `okstra status` first** and
174
+ avoid deleting logs for tasks in `in-progress` state — you will lose
175
+ the live trace for the active run.
155
176
  - Prompt history files (`.md`) are separate and are NOT touched by these
156
177
  commands — only `.log` sidecars.
157
- - This skill does not modify `.gitignore`. If the project commits
158
- `.project-docs/okstra/`, the user may want to add
178
+ - This skill **does not modify any external files itself**, including
179
+ `.gitignore`. If the project commits `.project-docs/okstra/`, the user
180
+ may want to add
159
181
  `.project-docs/okstra/tasks/**/runs/**/prompts/*.log` to `.gitignore`
160
182
  manually to keep large logs out of git.
161
183
 
@@ -40,54 +40,81 @@ OKSTRA_PROJECT_INFO="$($OKSTRA_CMD check-project --json)" || {
40
40
 
41
41
  task-key 형식: `<project-id>:<task-group>:<task-id>`
42
42
 
43
+ **Normalization**: task-key 매칭은 lowercase 로 수행한다. 디스크상의 group/id 세그먼트는 slugify (lowercase + non-alphanumeric runs → `-`) 된다 (`scripts/lib/okstra/interactive.sh:147`). 따라서 catalog 조회는 case-insensitive 이지만 파일 경로를 만들 때는 slugified segment 를 사용해야 한다.
44
+
43
45
  ### 방법 A: task-catalog.json (빠름)
44
46
 
45
47
  1. `.project-docs/okstra/discovery/task-catalog.json`을 읽는다.
46
- 2. `tasks` 배열에서 `taskKey`가 일치하는 항목을 찾는다.
47
- 3. `latestReportPath` 필드가 최신 보고서 경로이다.
48
+ 2. `tasks` 배열에서 `taskKey` lowercase 비교로 매칭한다.
49
+ 3. `latestReportPath` 필드는 task-type 과 무관한 "가장 최근 보고서" 이다.
48
50
 
49
51
  ### 방법 B: task-manifest.json (직접)
50
52
 
51
- task-catalog가 없거나 최신이 아닌 경우:
53
+ task-catalog 없는 경우:
54
+
55
+ 1. task-key 에서 task-group / task-id 를 추출하고 slugify 한다.
56
+ 2. `.project-docs/okstra/tasks/<group-segment>/<id-segment>/task-manifest.json` 을 읽는다.
57
+ 3. `latestReportPath` 필드 (task-type 무관, 최신).
58
+
59
+ ### 방법 C: timeline.json (특정 run 의 보고서)
52
60
 
53
- 1. task-key에서 task-group과 task-id를 추출한다.
54
- 2. `.project-docs/okstra/tasks/<task-group>/<task-id>/task-manifest.json`을 읽는다.
55
- 3. `latestReportPath` 필드가 최신 보고서 경로이다.
61
+ 특정 날짜나 run 보고서가 필요한 경우:
56
62
 
57
- ### 방법 C: timeline.json (특정 run의 보고서)
63
+ 1. `.project-docs/okstra/tasks/<group-segment>/<id-segment>/history/timeline.json` 읽는다.
64
+ 2. `runs[]` 배열에서 필터한다. 실제 필드:
65
+ - `runs[].runTimestamp` (ISO-8601 시작 시각)
66
+ - `runs[].status` (run 종료 상태)
67
+ - `runs[].taskType` (`requirements-discovery` 등 phase 이름)
68
+ - `runs[].reportPath` (해당 run 의 보고서 상대 경로)
58
69
 
59
- 특정 날짜나 run보고서가 필요한 경우:
70
+ ### 방법 D: 특정 task-type최신 보고서 (fallback)
60
71
 
61
- 1. `.project-docs/okstra/tasks/<task-group>/<task-id>/history/timeline.json`을 읽는다.
62
- 2. `runs` 배열에서 원하는 run을 찾는다 (날짜, 상태 등으로 필터).
63
- 3. `reportPath` 필드가 해당 run의 보고서 경로이다.
72
+ `latestReportPath` (방법 A/B) 는 task-type 을 가리지 않는다. 사용자가 *특정* task-type (예: `implementation-planning`) 의 최신 보고서를 요청하면:
73
+
74
+ 1. 디렉토리: `.project-docs/okstra/tasks/<group-segment>/<id-segment>/runs/<task-type-segment>/reports/`
75
+ 2. 파일명 패턴: `final-report-<task-type-segment>-<NNN>.md` (`scripts/okstra_ctl/sequence.py:31`)
76
+ 3. seq 가 가장 큰 파일이 해당 task-type 의 최신 보고서.
77
+ 4. timeline.json 의 `runs[].taskType == <task-type>` 으로 교차검증 가능.
64
78
 
65
79
  ## Step 2: Report 존재 확인
66
80
 
67
- 1. 찾은 경로에 파일이 실제로 존재하는지 확인한다.
81
+ 1. `latestReportPath`가 비어있지 않고, 해당 경로의 파일이 실제로 존재하는지 확인한다.
82
+ - 두 신호 중 하나라도 보고서 존재를 가리키면 경로를 표시한다 (tolerant).
68
83
  2. 존재하면 경로를 표시하고 읽을지 사용자에게 확인한다.
69
- 3. 존재하지 않으면:
70
- - `task-manifest.json`의 `currentStatus`를 확인한다.
71
- - status가 `completed`가 아니면: "이 task는 아직 완료되지 않았습니다 (status: `<status>`)."
72
- - 파일만 없으면: "보고서 파일이 존재하지 않습니다: `<path>`"
84
+ 3. 존재하지 않으면 `task-manifest.json`에서 다음 신호를 확인한다:
85
+ - `latestReportPath`가 비어있거나 누락된 경우, 그리고
86
+ - `currentStatus`가 `completed`가 아니고, `workStatus`가 `done`이 아니며, `workflow.routingStatus`도 완료를 가리키지 않는 경우
87
+ "이 task는 아직 완료되지 않았습니다 (currentStatus: `<currentStatus>`, workStatus: `<workStatus>`)."
88
+ - 위 신호 중 하나라도 완료를 가리키는데 파일만 없는 경우
89
+ → "보고서 파일이 존재하지 않습니다: `<path>`"
90
+
91
+ 워크플로 상태 enum은 `todo | in-progress | blocked | done` (workStatus) 와 `currentStatus`의 `completed` / `contract-violated` 등이다. `"completed"` 문자열은 `workStatus`에는 존재하지 않으므로 두 필드를 혼동하지 말 것.
73
92
 
74
93
  ## Step 3: Report 읽기 및 후속 작업 안내
75
94
 
76
95
  보고서를 읽은 후 사용자에게 가능한 후속 작업을 안내한다:
77
96
 
78
97
  1. **구현 진행**: 보고서의 "권장 다음 단계" 섹션 기반으로 코드 수정
79
- 2. **추가 검증**: 같은 task-key로 새 okstra run 실행 (`okstra-history` 스킬로 재실행 커맨드 생성)
80
- 3. **관련 task 확인**: 보고서의 관련 task 참조가 있으면 해당 task의 보고서도 조회
98
+ 2. **추가 검증**: 같은 task-key 로 새 okstra run 실행. 구체 커맨드:
99
+ ```bash
100
+ scripts/okstra.sh --task-key <task-key> --task-type <task-type>
101
+ ```
102
+ 더 풍부한 옵션 (base-ref, workers, render-only 등) 이 필요하면 `okstra-history` 스킬을 사용한다.
103
+ 3. **관련 task 확인**: 보고서의 관련 task 참조가 있으면 해당 task 의 보고서도 조회
81
104
 
82
105
  ## Output
83
106
 
84
107
  ```markdown
85
108
  ## Report for <task-key>
86
109
 
87
- - Status: `<status>`
88
- - Report path: `<relative-path>`
89
- - Run date: `<YYYY-MM-DD HH:MM>`
90
- - Task type: `<task-type>`
110
+ | Field | Value |
111
+ | ------------ | -------------------------------------------------- |
112
+ | Status | `<status>` |
113
+ | Task type | `<task-type>` |
114
+ | Run seq | `<NNN>` |
115
+ | Run date | `<runTimestamp ISO-8601>` |
116
+ | Report (rel) | `<relative-path-from-project-root>` |
117
+ | Report (abs) | `<absolute-path>` |
91
118
  ```
92
119
 
93
120
  이후 사용자 요청에 따라 보고서를 읽고 후속 작업을 진행한다.
@@ -8,7 +8,7 @@ user-invocable: false
8
8
 
9
9
  ## File-author ownership (BLOCKING)
10
10
 
11
- The final-report file at `runs/<task-type>/reports/final-report-<task-type>-<seq>.md` is authored by the `Report writer worker` subagent when that worker is in the run's roster. Claude lead reviews the file but does NOT write it itself in that case. Lead-authored fallback is permitted only after a real Report writer worker dispatch attempt with a recorded non-`completed` terminal status (`error` / `timeout` / `not-run`) and a logged reason (`okstra-error-log.py`).
11
+ The final-report file at `runs/<task-type>/reports/final-report-<task-type>-<seq>.md` is authored by the `Report writer worker` subagent when that worker is in the run's roster. Claude lead reviews the file but does NOT write it itself in that case. Lead-authored fallback is permitted only after a real Report writer worker dispatch attempt with a recorded non-`completed` terminal status (`error` / `timeout` / `not-run`) and a logged reason (`okstra-error-log.py`). **Except for `release-handoff`**, which has no worker roster — the Claude lead authors the final-report directly by design (see "Release-handoff section contract" below), and the fallback rules in this section do not apply.
12
12
 
13
13
  If you are reading this skill **as the report-writer-worker subagent**, YOU are the one calling the `Write` tool against the result path. Do not return the report inline — the file on disk is the canonical artifact.
14
14
 
@@ -60,7 +60,7 @@ A resumed lead session can ALWAYS dispatch a fresh Report writer worker. The Age
60
60
 
61
61
  ### Lead-authored fallback (only if dispatch failed)
62
62
 
63
- Lead-authored fallback is permitted only if all of the following are true and recorded in team-state:
63
+ Except for `release-handoff` (which is single-lead by design and never dispatches a Report writer worker — see "Release-handoff section contract" below), lead-authored fallback is permitted only if all of the following are true and recorded in team-state:
64
64
 
65
65
  1. A Report writer worker dispatch was actually attempted (Agent call was issued).
66
66
  2. The attempt recorded a terminal status of `error`, `timeout`, or `not-run` with a concrete reason (tool error message, timeout duration, or external blocker).
@@ -68,50 +68,43 @@ Lead-authored fallback is permitted only if all of the following are true and re
68
68
 
69
69
  Speculative reasons such as "session resume constraint", "team object no longer exists", or "lead can do it faster" are NOT valid.
70
70
 
71
- ## Phase 7 follow-up task spawner (BLOCKING when Section 7 is non-empty)
71
+ ## Phase 6 → Phase 7 execution sequence (BLOCKING order)
72
72
 
73
- After the token-usage collector finishes (the next subsection), Phase 7 must run the follow-up task spawner against the final-report file. This step is what turns the report's `## 7. Follow-up Tasks (후속 작업)` table into actual `tasks/<task-group>/<new-task-id>/` stubs that show up in `okstra-status`.
73
+ The four steps below MUST execute in this exact order. Reordering them is the recurring root cause of reports shipping with unsubstituted `{{LEAD_TOTAL_TOKENS}}` placeholders, Section 6 missing the follow-up entries, or Section 7 rows never spawning.
74
74
 
75
- ```bash
76
- python3 scripts/okstra-spawn-followups.py \
77
- <runDirectoryPath>/reports/final-report-<task-type>-<seq>.md \
78
- --project-root <project_root> \
79
- --task-group <task-group> \
80
- --parent-task-key <task-key>
81
- ```
82
-
83
- Behaviour contract:
84
-
85
- - The script is **idempotent**: rows whose target directory already exists are reported as `existing` and skipped without modification. Re-running the spawner across reruns of the same parent task is safe.
86
- - Rows with `Auto-spawn? != yes` are reported as `skipped` and never written to disk. Surface them in the final-report's Section 6 if the user should still take manual action.
87
- - A row with an invalid `Origin`, `Suggested task-type`, missing `Title`, or missing `Reason / Why deferred` causes the script to exit `1`. The report-writer worker MUST refuse to ship a final-report whose Section 7 contains such rows. Either fix the row or change `Auto-spawn?` to `no` and document why in Section 6.
88
- - For `task-type` ∈ {`implementation`, `final-verification`, `release-handoff`}, Section 7 must be present in the final report. An empty section is acceptable and is expressed as the single line `- 후속 작업 없음.` under the heading. The spawner treats a missing or empty section as a no-op (exit `0`).
89
-
90
- After the spawner completes, the report-writer worker MUST update Section 6 ("Recommended Next Steps") to list every newly created task-key together with its entry command, so the user can pick the follow-up up immediately:
75
+ 1. **Phase 6 — Report writer worker drafts the final-report file** at `runs/<task-type>/reports/final-report-<task-type>-<seq>.md`. Token placeholders are left verbatim; Section 6 lists prioritized actions but does NOT yet include auto-spawned follow-ups (they don't exist yet).
76
+ 2. **Phase 7 step 1 — Token-usage collector with `--substitute-final-report`** (BLOCKING). One invocation aggregates `leadUsage` / `workers[].usage` / `usageSummary` into team-state AND substitutes the 10 placeholders in the final-report file. Skipping the flag ships literal `{{...}}` in the Token Usage Summary table.
91
77
 
92
- ```
93
- - Follow-up: `<task-group>/<new-task-id>` — Claude Code 세션 안 `/okstra-run task-key=<task-group>/<new-task-id> task-type=<suggested>` / 별도 터미널 `scripts/okstra.sh --task-key <task-group>/<new-task-id> --task-type <suggested>`
94
- ```
78
+ ```bash
79
+ python3 scripts/okstra-token-usage.py \
80
+ <runDirectoryPath>/state/team-state-<task-type>-<seq>.json \
81
+ --write --summary \
82
+ --substitute-final-report <runDirectoryPath>/reports/final-report-<task-type>-<seq>.md
83
+ ```
95
84
 
96
- ## Phase 7 token-usage collector (BLOCKING)
85
+ The 10 substituted placeholders: `{{LEAD_TOTAL_TOKENS}}`, `{{LEAD_BILLABLE_TOKENS}}`, `{{LEAD_COST_USD}}`, `{{WORKER_TOTAL_TOKENS}}`, `{{WORKER_BILLABLE_TOKENS}}`, `{{WORKER_COST_USD}}`, `{{GRAND_TOTAL_TOKENS}}`, `{{GRAND_BILLABLE_TOKENS}}`, `{{GRAND_COST_USD}}`, `{{CLI_COST_USD}}`. The final-report file MUST already exist (Phase 6 output).
86
+ 3. **Phase 7 step 2 — Follow-up task spawner** (BLOCKING when Section 7 is non-empty). Turns the report's `## 7. Follow-up Tasks (후속 작업)` rows into `tasks/<task-group>/<new-task-id>/` stubs.
97
87
 
98
- At the start of Phase 7, run the token-usage collector with the final-report substitution flag. This step is BLOCKING — both the team-state aggregation AND the final-report placeholder substitution happen here, in one invocation:
99
-
100
- ```bash
101
- python3 scripts/okstra-token-usage.py \
102
- <runDirectoryPath>/state/team-state-<task-type>-<seq>.json \
103
- --write --summary \
104
- --substitute-final-report <runDirectoryPath>/reports/final-report-<task-type>-<seq>.md
105
- ```
88
+ ```bash
89
+ python3 scripts/okstra-spawn-followups.py \
90
+ <runDirectoryPath>/reports/final-report-<task-type>-<seq>.md \
91
+ --project-root <project_root> \
92
+ --task-group <task-group> \
93
+ --parent-task-key <task-key>
94
+ ```
106
95
 
107
- This:
96
+ Behaviour contract:
97
+ - Idempotent: rows whose target dir exists are reported as `existing` and skipped. Reruns of the same parent task are safe.
98
+ - Rows with `Auto-spawn? != yes` are reported as `skipped` and never written; surface them in Section 6 if manual action is still needed.
99
+ - An invalid `Origin`, `Suggested task-type`, missing `Title`, or missing `Reason / Why deferred` exits `1`. The report-writer MUST refuse to ship a Section 7 with such rows.
100
+ - **Canonical spawn rule (single source of truth):** the spawner runs when `task-type` ∈ {`implementation`, `final-verification`, `release-handoff`}, OR when Section 7 is non-empty for any other task-type. For the listed task-types Section 7 must be present in the report; an empty section renders as `- 후속 작업 없음.`. Missing / empty sections are no-ops (exit `0`). All other references to this rule (including the Persistence Checklist) defer to this statement.
101
+ 4. **Phase 7 step 3 — Update Section 6** after the spawner. The report-writer MUST append one row per newly spawned task-key with its entry command:
108
102
 
109
- - Populates `leadUsage`, every `workers[].usage`, and `usageSummary` in team-state from session transcripts.
110
- - Substitutes the 10 token-related placeholders (`{{LEAD_TOTAL_TOKENS}}`, `{{LEAD_BILLABLE_TOKENS}}`, `{{LEAD_COST_USD}}`, `{{WORKER_TOTAL_TOKENS}}`, `{{WORKER_BILLABLE_TOKENS}}`, `{{WORKER_COST_USD}}`, `{{GRAND_TOTAL_TOKENS}}`, `{{GRAND_BILLABLE_TOKENS}}`, `{{GRAND_COST_USD}}`, `{{CLI_COST_USD}}`) in the final-report file with concrete values from the freshly computed usageSummary.
103
+ ```
104
+ - Follow-up: `<task-group>/<new-task-id>` Claude Code 세션 `/okstra-run task-key=<task-group>/<new-task-id> task-type=<suggested>` / 별도 터미널 `scripts/okstra.sh --task-key <task-group>/<new-task-id> --task-type <suggested>`
105
+ ```
111
106
 
112
- Skipping `--substitute-final-report` is the recurring root cause of reports shipping with literal `{{LEAD_TOTAL_TOKENS}}` etc. in their Token Usage Summary table. Always pass the flag — never run the collector without it during Phase 7.
113
-
114
- The final-report file MUST already exist before this step runs (it's authored by Report writer worker in Phase 6, or by Lead in the documented fallback case). The status file can be written after this step completes.
107
+ The status file is written after step 3 completes.
115
108
 
116
109
  ## Final Report Structure
117
110
 
@@ -181,7 +174,7 @@ Token Summary Generation Rules:
181
174
 
182
175
  ### Implementation-planning section heading contract (BLOCKING)
183
176
 
184
- When the run's `task-type` is `implementation-planning`, the final report MUST contain section headings whose **lines include each of the 8 literal English substrings below**. The validator (`validators/validate-run.py`) does plain substring matching on the report text — 7-of-8 missing was a real, repeatedly observed failure mode caused by translating the headings to Korean.
177
+ When the run's `task-type` is `implementation-planning`, the final report MUST contain section headings whose **lines include each of the 9 literal English substrings below**. The validator (`validators/validate-run.py`) does plain substring matching on the report text — missing headings was a real, repeatedly observed failure mode caused by translating the headings to Korean.
185
178
 
186
179
  | # | Required substring | Recommended heading form |
187
180
  |---|--------------------|--------------------------|
@@ -193,11 +186,26 @@ When the run's `task-type` is `implementation-planning`, the final report MUST c
193
186
  | 6 | `Validation Checklist` | `### Validation Checklist (검증 체크리스트)` |
194
187
  | 7 | `Rollback` | `### Rollback Strategy (롤백 전략)` |
195
188
  | 8 | `User Approval Request` | `### User Approval Request (사용자 승인 요청)` |
189
+ | 9 | `Plan Body Verification` + `Gate result:` | `### Plan Body Verification (계획 본문 검증)` containing a `Gate result:` line — copy `okstra-final-report.template.md §4.5.9` verbatim. Validator (`validators/validate-run.py` lines 531, 548) checks both substrings. |
196
190
 
197
191
  The Korean translation in parentheses is optional but the English keyword is mandatory. The body of each section is written in Korean per the writing rules below. For non-`implementation-planning` runs, omit this entire block — these headings are NOT validator-checked for other task-types.
198
192
 
199
193
  The final-report template `okstra-final-report.template.md` Section 4.5 already encodes this contract — copy that block verbatim and fill in.
200
194
 
195
+ ### Final-verification verdict token contract (BLOCKING)
196
+
197
+ When the run's `task-type` is `final-verification`, the report's `## 2. Final Verdict` table MUST contain a `Verdict Token` row whose value is **exactly one of** the literal strings below. The `release-handoff` profile reads this row as its entry gate; any other value blocks the next phase.
198
+
199
+ | # | Required substring | Meaning |
200
+ |---|--------------------|---------|
201
+ | 1 | `accepted` | All acceptance criteria pass; `release-handoff` may proceed. |
202
+ | 2 | `conditional-accept` | Acceptance passes with caveats; user must resolve listed conditions before `release-handoff`. |
203
+ | 3 | `blocked` | Acceptance failed; routing returns to `error-analysis` or `implementation-planning`. |
204
+
205
+ For every other task-type, set the `Verdict Token` cell to `not-applicable`. Do NOT omit the row — the template renders it for all task-types and downstream tooling expects the field to exist.
206
+
207
+ The final-report template `okstra-final-report.template.md` Section 2 already encodes this contract — copy that block verbatim and fill in.
208
+
201
209
  ### Release-handoff section contract (release-handoff runs only)
202
210
 
203
211
  When the run's `task-type` is `release-handoff`, the final report MUST include Section `## 4.6 Release Handoff Deliverables` with all seven sub-sections (`4.6.1` Source Verification Report, `4.6.2` Feature Branch & Working-Tree State, `4.6.3` User Selections, `4.6.4` Executed Commands, `4.6.5` Commit List, `4.6.6` Pull Request Outcome, `4.6.7` Routing Recommendation). Every entry is dictated by the lead's recorded git/gh command log and the user's verbatim answers to the H1/H2/H3 menu prompts. H1 choices are `local only`, `push + PR`, or `skip`; release-handoff records existing implementation commits and MUST NOT create new commits. If the user picked `skip` (H1) or `cancel` (H3), keep 4.6.3 populated but leave 4.6.4–4.6.6 explicitly empty per the template's empty-state lines.
@@ -216,35 +224,22 @@ runs/<task-type>/worker-results/report-writer-worker-<task-type>-<seq>.md
216
224
 
217
225
  This file is checked by the validator whenever the role's terminal status is `completed`. Without it the run fails with `report-writer is completed but worker result file is missing`.
218
226
 
219
- The file content is short: it begins with the standard worker-result header from `okstra-team-contract`, then names the canonical final-report path you wrote, lists the input artifacts you reconciled, and records any structural deviations from `final-report-template.md`. Do NOT duplicate the full final-report body here — it's an audit pointer, not a second copy.
227
+ **Frontmatter + header schema** — both the worker-results audit file AND the final-report file are governed by `okstra-team-contract` ("Result Frontmatter" and the standard worker-result header sections). That document is the single source of truth; do NOT restate the field list here. Use `workerId: "report-writer"` for both files and copy every other frontmatter value verbatim from `analysis-material.md`. The body of this audit file is short: name the canonical final-report path you wrote, list the input artifacts you reconciled, and record any structural deviations from `final-report.template.md`. Do NOT duplicate the full final-report body here — it's an audit pointer, not a second copy.
220
228
 
221
229
  Skipping this file because "the real report is in `reports/`" is wrong. Both files are required.
222
230
 
223
231
  ### Main Body Section
224
232
 
225
- Section numbering matches `okstra-final-report.template.md`. Section 0 is the carry-in reconciliation that runs first when a clarification response was provided; sections 1–7 follow the template's main body order.
226
-
227
- 0. **Clarification Response Carried In** - if `{{CLARIFICATION_RESPONSE_RELATIVE_PATH}}` is non-empty, read `instruction-set/clarification-response.md`, walk every `C-*` row of the prior report's `## 5. Clarification Items` table, reconcile each one against new evidence, and record the outcome (`resolved`/`obsolete`) plus the citation in this section before drafting the verdict. If the prior report uses the deprecated `4.5.9 Open Questions` / `5.1` / `5.2` layout with `OQ-*`/`A*`/`Q*` IDs, follow the legacy-carry-in mapping rule in `final-report-template.md` section 0.
228
- 1. **Problem or Verification Summary** - Key summary based on the brief and data (3–5 bullet points)
229
- 2. **Cross Verification Results** (Use 4 categories when convergence is enabled, per `okstra-convergence`)
230
- - Round History sub-table (convergence-enabled runs only): one row per executed round with columns `Round | inputQueueSize | resolvedCount | carriedForwardCount | dispatches (worker:status:durationMs) | skippedWorkers (worker:reason)`. Add a one-line note immediately under the table with `round2SkippedReason: <value>` (always present, even when `"not-skipped"`). Pull all values verbatim from `convergence-<task-type>-<seq>.json`.
231
- - Full Consensus: Findings agreed upon by all workers
232
- - Partial Consensus: Agreed upon by a majority of workers; dissenting opinions are specified
233
- - Contested: No consensus after the last executed round; each worker’s position specified. Empty contested list is shown as the literal line `- 합의 미달 항목 없음.`
234
- - Worker-Unique: Verified only by the discoverer; verification history specified
235
- - In runs with convergence disabled, maintain the existing Consensus/Differences format and omit the Round History sub-table.
236
- 3. **Final Verdict** - Conclusion based on comprehensive evidence; direction provided. For `final-verification`, include a `Verdict Token` field whose value is exactly `accepted`, `conditional-accept`, or `blocked`; `release-handoff` uses that field as its entry gate.
237
- 4. **Evidence and Detailed Analysis**
238
- - Key Evidence: File path, line number, actual evidence
239
- - If explicit expected values are present in `reference-expectations.md`, specify whether they match or differ from the expected values in config files / deployment manifests
240
- - Supporting evidence or alternative interpretations
241
- 5. **Missing Information and Risks** - Uncertain/I don't know items
242
- 6. **Clarification Items** - single unified table (`C-001`, `C-002`, ...) the user fills inline before reruns. Columns: `ID`, `Ticket ID`, `Kind` (`material` / `decision` / `data-point`), `Statement`, `Expected form`, `Blocks` (`approval` / `next-phase` / `none`), `Status`, `User input`. Replaces the legacy `4.5.9 Open Questions` / `5.1` / `5.2` triple; never create those sub-sections — same item appearing in two places is the failure mode this table prevents.
243
- - Required for `task-type` `error-analysis` and `requirements-discovery` whenever blocking uncertainty remains
244
- - Optional for other task-types; explicitly state "no clarification needed" when none
245
- - Follow the table format from `final-report-template.md` exactly (columns: Question ID, Blocking, Why this matters, Question, Expected answer shape, Status, Answer)
246
- - Use stable `Q1`, `Q2`, ... ids and never delete prior ids on rerun; mark them `resolved` or `obsolete` instead
247
- 7. **Recommended Next Steps** - Actions by Priority
233
+ Section numbering follows `templates/reports/final-report.template.md` exactly that file is the single source of truth. Below is a one-line summary of each section's writer obligation; consult the template for full body structure.
234
+
235
+ 0. **Clarification Response Carried In** if `{{CLARIFICATION_RESPONSE_RELATIVE_PATH}}` is non-empty, walk every `C-*` row of the prior report's `## 5. Clarification Items` table, reconcile each one against new evidence, and record the outcome (`resolved` / `obsolete`) with citation before drafting the verdict. Legacy `4.5.9 / 5.1 / 5.2` carry-in mapping is documented in the template's Section 0.
236
+ 1. **Cross Verification Results** — 4 categories (Full / Partial / Contested / Worker-Unique) when convergence is enabled, per `okstra-convergence`. Prepend the Round History sub-table (columns: `Round | inputQueueSize | resolvedCount | carriedForwardCount | dispatches | skippedWorkers`) plus a `round2SkippedReason: <value>` note, pulled verbatim from `convergence-<task-type>-<seq>.json`. Empty contested list renders as `- 합의 미달 항목 없음.`. Convergence-disabled runs use the legacy Consensus/Differences format and omit the round table.
237
+ 2. **Final Verdict** `Direction` `continue-investigation` / `begin-implementation` / `approve` / `reject` / `hold`. **Verdict Token** is `not-applicable` for every task-type except `final-verification` — see "Final-verification verdict token contract" below for that case.
238
+ 3. **Evidence and Detailed Analysis** primary evidence rows (file path, line, snippet); secondary evidence / alternate interpretations. If `reference-expectations.md` lists explicit expected values, record match/gap per row.
239
+ 4. **Missing Information and Risks** — uncertain / "I don't know" items. `implementation-planning` adds §4.5 (see heading contract below); `release-handoff` adds §4.6.
240
+ 5. **Clarification Items** single unified `C-*` table; column schema, ID convention, and rerun behaviour are owned by `_common-contract.md §Clarification request policy` (8-column SSOT). Never recreate the deprecated `4.5.9 Open Questions` / `5.1` / `5.2` sub-sections.
241
+ 6. **Recommended Next Steps** prioritized actions. After Phase 7's follow-up spawner runs, append a row per newly created task-key (see "Phase 6 Phase 7 execution sequence" above).
242
+ 7. **Follow-up Tasks** — auto-spawn-eligible table. Each row drives `okstra-spawn-followups.py`; see template §7 for the row schema.
248
243
 
249
244
  ### Writing Guidelines
250
245
 
@@ -280,7 +275,7 @@ Persistence steps that must be performed in Phase 7:
280
275
  - [ ] 5. **Update task-index.md**: Refresh human-readable summary
281
276
  - [ ] 6. **Generate final status file**: `runs/<task-type>/status/final-<task-type>-<seq>.status` (if necessary)
282
277
  - [ ] 7. **Save convergence state**: `runs/<task-type>/state/convergence-<task-type>-<seq>.json` (when convergence is enabled)
283
- - [ ] 8. **Spawn follow-up task stubs**: run `scripts/okstra-spawn-followups.py` against the final-report when the run's `task-type` {`implementation`, `final-verification`, `release-handoff`}, OR when Section 7 is non-empty for any other task-type. See "Phase 7 follow-up task spawner" above for the exact command and contract. The script is idempotent across reruns.
278
+ - [ ] 8. **Spawn follow-up task stubs**: run `scripts/okstra-spawn-followups.py` against the final-report per the canonical spawn rule defined in "Phase 7 follow-up task spawner" above. Do not restate the trigger condition here — that section is the single source of truth. The script is idempotent across reruns.
284
279
 
285
280
  ### Response after Persistence
286
281
 
@@ -9,6 +9,8 @@ Launch an okstra task — gather inputs interactively via the **wizard state mac
9
9
 
10
10
  **Single authority**: this skill drives `okstra wizard`, which owns every step (ordering, branching, validation). The skill is just a thin prompt-relay loop — it never decides "what to ask next" itself. If the flow needs to change, edit `scripts/okstra_ctl/wizard.py`, not this file.
11
11
 
12
+ **Bash invocation rule (permission-friendly)**: every Bash command in this skill MUST begin with the literal token `okstra` (or another already-allowed binary) and pass literal argument values. Do not introduce shell variables (`$STATE_FILE`, `$ANSWER`, `$projectRoot`, ...), `$(...)` command substitution, or leading `VAR=...` assignments — any of those make the leading token non-literal, defeat the `Bash(okstra:*)` permission match, and force a confirmation prompt on every wizard call. When a prior tool call emitted a path or value, read it from the tool output and paste the literal string into the next command.
13
+
12
14
  ## When to Use
13
15
 
14
16
  - The user is inside a Claude Code session and asks to start an okstra task ("run okstra here", "start an error-analysis on this branch", "okstra implementation-planning for INV-1234").
@@ -16,7 +18,7 @@ Launch an okstra task — gather inputs interactively via the **wizard state mac
16
18
 
17
19
  ## When NOT to Use
18
20
 
19
- - User explicitly asks to spawn a new terminal / new claude — use `okstra-history` Step 4 (resume command) or instruct them to run `okstra.sh` in another terminal.
21
+ - User explicitly asks to spawn a new terminal / new claude — use `okstra-history` Step 4 (resume command) or instruct them to run `okstra` in another terminal.
20
22
  - User wants status only — use `okstra-status`.
21
23
  - User wants past runs — use `okstra-history`.
22
24
 
@@ -46,33 +48,40 @@ Never invent additional questions. Never reorder. Never use `AskUserQuestion` fo
46
48
 
47
49
  ## Step 1: Verify okstra runtime + project setup
48
50
 
49
- ```bash
50
- if command -v okstra >/dev/null 2>&1; then
51
- okstra ensure-installed >/dev/null 2>&1 || { echo "FAIL: okstra ensure-installed failed" >&2; exit 1; }
52
- eval "$(okstra paths --shell)"
53
- export PYTHONPATH="$OKSTRA_PYTHONPATH"
54
- okstra check-project --json || { echo "FAIL: this project has no okstra setup. Tell the user to run /okstra-setup first." >&2; exit 1; }
55
- else
56
- npx -y okstra@latest ensure-installed >/dev/null 2>&1 || { echo "FAIL: okstra not installed; tell the user to run: npx okstra@latest install" >&2; exit 1; }
57
- eval "$(npx -y okstra@latest paths --shell)"
58
- export PYTHONPATH="$OKSTRA_PYTHONPATH"
59
- npx -y okstra@latest check-project --json || { echo "FAIL: this project has no okstra setup. Tell the user to run /okstra-setup first." >&2; exit 1; }
60
- fi
61
- ```
51
+ Run each of the following commands as a **separate Bash tool call**. Each command starts with the literal token `okstra` so the `Bash(okstra:*)` permission match succeeds. Do **not** wrap any of them in `if`, `eval`, `export`, `$(...)`, `VAR=...`, `||`, or `&&` — those leading tokens defeat the permission match and force a confirmation prompt on every call. The LLM (you) inspects each command's JSON output and decides what to do next in natural language — never in shell.
52
+
53
+ 1. `okstra ensure-installed`
54
+ If this exits non-zero, tell the user: "okstra runtime missing — run `npx okstra@latest install` once and retry." Then stop.
55
+
56
+ 2. `okstra paths --json`
57
+ Read `pythonPath` and other path fields from the JSON output. **Do not** `export PYTHONPATH=...` from this skill — every subsequent `okstra <subcmd>` call already self-bootstraps its Python path. Keep the parsed values in mind only as diagnostic context.
62
58
 
63
- The `check-project --json` output goes to stdout; read it from the tool result. If its `ok` field is `false`, ask the user with a **plain text prompt** for an absolute project-root path; rerun `okstra check-project --cwd <path> --json`. Re-prompt with plain text on failure.
59
+ 3. `okstra check-project --json`
60
+ Reads the project from the current working directory. Parse the JSON from stdout. If `ok` is `false`, ask the user with a **plain text prompt** for an absolute project-root path, then rerun as a separate tool call (still literal-token-first):
61
+ `okstra check-project --cwd /abs/path/from/user --json`
62
+ Substitute the literal absolute path the user gave you (no `$(pwd)`, no shell variables). Re-prompt with plain text on continued failure.
64
63
 
65
- Parse `projectRoot` and `projectId` from that JSON output.
64
+ Parse `projectRoot` and `projectId` from the successful `check-project --json` output and carry them as literal strings into Step 2.
65
+
66
+ > If the `okstra` binary is not on `PATH` at all, none of the commands above will run. In that case tell the user verbatim: "okstra not installed — run `npx okstra@latest install` once, then retry this skill." Do **not** try to invoke `npx -y okstra@latest ...` from this skill — `npx` is not on the literal-token allow-list and will force a confirmation prompt on every wizard call afterward.
66
67
 
67
68
  ## Step 2: Initialize the wizard
68
69
 
70
+ First, generate a state-file path (Bash invocation rule from the top of this file applies to every command below):
71
+
69
72
  ```bash
70
- STATE_FILE="$(mktemp -t okstra-wizard.XXXX.json)"
73
+ okstra wizard new-state-file
74
+ ```
75
+
76
+ This prints one absolute path on stdout (e.g. `/var/folders/.../okstra-wizard.AbCd.json`). Read that path from the tool output and **paste it literally** into every subsequent `--state-file` argument.
77
+
78
+ Then initialize the wizard with the literal `projectRoot` / `projectId` you parsed from Step 1 and the literal state-file path from above:
71
79
 
80
+ ```bash
72
81
  okstra wizard init \
73
- --state-file "$STATE_FILE" \
74
- --project-root "$projectRoot" \
75
- --project-id "$projectId"
82
+ --state-file /var/folders/.../okstra-wizard.AbCd.json \
83
+ --project-root /abs/path/to/project \
84
+ --project-id my-project-id
76
85
  ```
77
86
 
78
87
  Output: the same `{ok, next}` JSON described above. The first `next` is always `step: "task_pick"`.
@@ -84,10 +93,19 @@ Repeat until `next.kind == "done"`:
84
93
  1. **Render** the prompt according to `kind`:
85
94
  - `pick` → `AskUserQuestion` with `label` and `options`. The user's chosen option's `value` is the answer string.
86
95
  - `text` → plain text message containing `label`. Consume the user's next reply verbatim as the answer string (empty reply = empty string).
87
- 2. **Submit** the answer:
96
+ 2. **Submit** the answer — call `okstra wizard step` with the literal state-file path from Step 2 and the literal user answer (no shell variables, no `$(...)`):
88
97
  ```bash
89
- okstra wizard step --state-file "$STATE_FILE" --answer "$ANSWER"
98
+ okstra wizard step --state-file /var/folders/.../okstra-wizard.AbCd.json --answer preprod
90
99
  ```
100
+ If the answer contains spaces or shell metacharacters, wrap it in double quotes around the literal string only — never inside `"$VAR"`.
101
+
102
+ **MANDATORY: empty answers must pass `--answer ""` explicitly.** If the user's reply is the empty string, the call MUST still include the flag with an empty literal value:
103
+ ```bash
104
+ okstra wizard step --state-file /var/folders/.../okstra-wizard.AbCd.json --answer ""
105
+ ```
106
+ Omitting `--answer` entirely is forbidden. The wizard interprets a missing `--answer` flag as "re-emit the current prompt" (a `get-current-prompt` style no-op), not as "submit empty" — so dropping the flag will loop the same prompt forever. Submitting `--answer ""` is the only way to advance past an intentionally-blank step (e.g. "use phase default").
107
+
108
+ **Escaping rule**: if the literal answer contains `"`, escape each occurrence as `\"` inside the double-quoted argument. Empty values must still be `--answer ""` — the flag itself is mandatory, even when the value is empty.
91
109
  3. **Handle result**:
92
110
  - `ok: true` → echo `result.echo` to the user on one short line, then loop with `result.next`.
93
111
  - `ok: false` → show `result.error` to the user verbatim, then loop with `result.current` (re-prompt the same step).
@@ -110,7 +128,7 @@ Do not second-guess the wizard. If the next prompt seems out of place, the bug i
110
128
  When `next.step == "confirm"`, before relaying the picker, fetch the human-readable selection summary:
111
129
 
112
130
  ```bash
113
- okstra wizard confirmation --state-file "$STATE_FILE"
131
+ okstra wizard confirmation --state-file /var/folders/.../okstra-wizard.AbCd.json
114
132
  ```
115
133
 
116
134
  Output: `{ok: true, text: "선택 확인:\n task-type : ...\n ..."}`. Print `text` to the user, then render the `confirm` picker (Proceed / Edit).
@@ -120,11 +138,15 @@ Output: `{ok: true, text: "선택 확인:\n task-type : ...\n ..."}`. Prin
120
138
  When `next.kind == "done"`, fetch the final args:
121
139
 
122
140
  ```bash
123
- okstra wizard render-args --state-file "$STATE_FILE"
141
+ okstra wizard render-args --state-file /var/folders/.../okstra-wizard.AbCd.json
124
142
  ```
125
143
 
126
144
  Output: `{ok: true, args: {"project-root": "...", "task-type": "...", ...}}`. Build the `okstra render-bundle` invocation from `args`, passing each key as `--<key>` and the value verbatim (including empty strings — they are intentional `use phase default` markers).
127
145
 
146
+ **Empty-value rule (same as Step 3)**: every flag whose value is the empty string MUST still be passed explicitly as `--<key> ""`. For example: `--workers ""`, `--directive ""`, `--related-tasks ""`. Omitting the flag is forbidden — `render-bundle` distinguishes "flag absent" from "flag present with empty value", and the wizard's intent is always the latter.
147
+
148
+ **Escaping rule**: inside a double-quoted value, escape any literal `"` as `\"`. Do not collapse `--key ""` into `--key` even when the value is empty.
149
+
128
150
  ```bash
129
151
  okstra render-bundle \
130
152
  --project-root "<args.project-root>" \
@@ -152,17 +174,11 @@ okstra render-bundle \
152
174
 
153
175
  The python function underneath is mutex-protected (`~/.okstra/.locks/<task-key>.lock`), writes `run-context-*.json` + `run-inputs-*.json` + all manifests + discovery files, and registers the run in `~/.okstra/recent.jsonl` with status `prepared`.
154
176
 
155
- You can delete `$STATE_FILE` after this point — its job is done.
177
+ You can delete the literal state-file path after this point — its job is done. Invoke `rm` with the literal path (e.g. `rm /var/folders/.../okstra-wizard.AbCd.json`), not a shell variable.
156
178
 
157
179
  ## Step 6: Take over as Claude lead
158
180
 
159
- Read these files (do not paraphrase) and enter `Claude lead` mode:
160
-
161
- 1. `<INSTRUCTION_SET_DIR>/claude-execution-prompt.md` — the lead prompt
162
- 2. `<INSTRUCTION_SET_DIR>/analysis-profile.md` — per-task-type allowed outputs / forbidden actions
163
- 3. `<INSTRUCTION_SET_DIR>/analysis-material.md` — task brief + directive
164
- 4. `<INSTRUCTION_SET_DIR>/reference-expectations.md`
165
- 5. `<INSTRUCTION_SET_DIR>/final-report-template.md`
181
+ Read `<INSTRUCTION_SET_DIR>/claude-execution-prompt.md` verbatim and enter `Claude lead` mode. The lead prompt itself enumerates every other instruction-set file to load (`analysis-profile.md`, `analysis-material.md`, `reference-expectations.md`, `final-report-template.md`, the run manifest, the team-state artifact, etc.) — follow its order, do not preempt it.
166
182
 
167
183
  Then proceed through the phases exactly as the lead prompt directs (Phase 1 context → Phase 2+ worker dispatch → final synthesis → final report).
168
184
 
@@ -180,19 +196,17 @@ okstra config set pr-template-path "<path>" --scope project
180
196
  okstra config set pr-template-path "<path>" --scope global
181
197
  ```
182
198
 
183
- The scope is exposed via `wizard render-args` only as the `pr-template-path` value (1-shot override); the persist hint lives in the wizard state. Read it with:
199
+ The scope is held in the wizard state but is not yet exposed by any `okstra wizard` subcommand. Until the subcommand below ships, read the JSON state file directly with the `Read` tool (literal path captured in Step 2) and inspect the `pr_template_scope` field it is a plain serialized `WizardState`. Do not shell out (`python3 -c`, `jq`, etc.); the literal-token Bash rule rejects them.
184
200
 
185
- ```bash
186
- python3 -c "import json,sys; print(json.load(open(sys.argv[1])).get('pr_template_scope',''))" "$STATE_FILE"
187
- ```
201
+ ## Out-of-scope backlog
188
202
 
189
- (or just inspect the JSON state file directly it is a plain serialized `WizardState`).
203
+ - **`okstra wizard pr-template-scope --state-file PATH`**: add a thin subcommand to `scripts/okstra_ctl/wizard.py` that prints `{ok: true, scope: "once" | "project" | "global"}` so this skill can drop the `Read`-the-raw-state-file detour. The subcommand should reuse the existing `load_state_file` path; no schema changes required.
190
204
 
191
205
  ## Concurrency
192
206
 
193
207
  - `prepare_task_bundle` serializes per-task via `~/.okstra/.locks/<task-key>.lock`. Concurrent skill invocations on the same task wait; different tasks proceed in parallel.
194
- - Each wizard run owns its own `$STATE_FILE`; two parallel skill invocations do not collide.
195
- - The skill must NOT call `okstra.sh` or any other bash entrypoint that would re-implement the orchestration. The wizard + `render-bundle` is the single authority.
208
+ - Each wizard run owns its own state file (one per `okstra wizard new-state-file`); two parallel skill invocations do not collide.
209
+ - The skill must NOT call `okstra.sh` (or any other bash entrypoint) that would re-implement the orchestration. The wizard + `render-bundle` is the single authority.
196
210
 
197
211
  ## Failure Modes
198
212