okstra 0.122.0 → 0.124.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 (108) hide show
  1. package/README.md +5 -2
  2. package/docs/architecture/storage-model.md +15 -1
  3. package/docs/architecture.md +45 -7
  4. package/docs/cli.md +47 -5
  5. package/docs/for-ai/README.md +42 -36
  6. package/docs/for-ai/skills/okstra-brief-gen.md +105 -105
  7. package/docs/for-ai/skills/okstra-container-build.md +61 -61
  8. package/docs/for-ai/skills/okstra-graphify.md +64 -0
  9. package/docs/for-ai/skills/okstra-inspect.md +86 -86
  10. package/docs/for-ai/skills/okstra-manager.md +32 -32
  11. package/docs/for-ai/skills/okstra-memory.md +49 -50
  12. package/docs/for-ai/skills/okstra-pr-gen.md +48 -0
  13. package/docs/for-ai/skills/okstra-rollup.md +58 -58
  14. package/docs/for-ai/skills/okstra-run.md +95 -95
  15. package/docs/for-ai/skills/okstra-schedule-gen.md +320 -0
  16. package/docs/for-ai/skills/okstra-setup.md +63 -64
  17. package/docs/for-ai/skills/okstra-user-response.md +48 -0
  18. package/docs/performance-improvement-plan-v2.md +4 -4
  19. package/docs/pr-template-usage.md +34 -34
  20. package/docs/project-structure-overview.md +92 -70
  21. package/docs/task-process/README.md +33 -33
  22. package/docs/task-process/common-flow.md +26 -26
  23. package/docs/task-process/error-analysis.md +20 -21
  24. package/docs/task-process/final-verification.md +41 -41
  25. package/docs/task-process/implementation-planning.md +52 -28
  26. package/docs/task-process/implementation.md +51 -32
  27. package/docs/task-process/release-handoff.md +46 -46
  28. package/docs/task-process/requirements-discovery.md +22 -23
  29. package/package.json +1 -1
  30. package/runtime/BUILD.json +2 -2
  31. package/runtime/agents/workers/antigravity-worker.md +4 -4
  32. package/runtime/agents/workers/claude-worker.md +2 -2
  33. package/runtime/agents/workers/codex-worker.md +4 -4
  34. package/runtime/agents/workers/report-writer-worker.md +4 -4
  35. package/runtime/bin/lib/okstra/usage.sh +3 -3
  36. package/runtime/prompts/coding-preflight/frameworks/node-server.md +1 -1
  37. package/runtime/prompts/launch.template.md +6 -3
  38. package/runtime/prompts/lead/convergence.md +11 -21
  39. package/runtime/prompts/lead/okstra-lead-contract.md +16 -18
  40. package/runtime/prompts/lead/plan-body-verification.md +47 -18
  41. package/runtime/prompts/lead/report-writer.md +50 -45
  42. package/runtime/prompts/lead/team-contract.md +11 -122
  43. package/runtime/prompts/profiles/_common-contract.md +15 -22
  44. package/runtime/prompts/profiles/_implementation-deliverable.md +4 -2
  45. package/runtime/prompts/profiles/_implementation-executor.md +6 -1
  46. package/runtime/prompts/profiles/_implementation-verifier.md +3 -3
  47. package/runtime/prompts/profiles/error-analysis.md +2 -2
  48. package/runtime/prompts/profiles/final-verification.md +3 -1
  49. package/runtime/prompts/profiles/implementation-planning.md +24 -14
  50. package/runtime/prompts/profiles/implementation.md +1 -1
  51. package/runtime/prompts/profiles/improvement-discovery.md +1 -1
  52. package/runtime/prompts/profiles/release-handoff.md +3 -3
  53. package/runtime/prompts/profiles/requirements-discovery.md +18 -18
  54. package/runtime/prompts/wizard/prompts.ko.json +44 -0
  55. package/runtime/python/okstra_ctl/codex_dispatch.py +23 -1
  56. package/runtime/python/okstra_ctl/design_prep.py +1462 -0
  57. package/runtime/python/okstra_ctl/design_surfaces.py +243 -0
  58. package/runtime/python/okstra_ctl/final_report_schema.py +33 -1
  59. package/runtime/python/okstra_ctl/implementation_stage.py +35 -0
  60. package/runtime/python/okstra_ctl/incremental_carry.py +294 -21
  61. package/runtime/python/okstra_ctl/incremental_scope.py +51 -5
  62. package/runtime/python/okstra_ctl/material.py +1 -1
  63. package/runtime/python/okstra_ctl/model_discovery.py +98 -0
  64. package/runtime/python/okstra_ctl/models.py +8 -3
  65. package/runtime/python/okstra_ctl/render.py +5 -0
  66. package/runtime/python/okstra_ctl/run.py +53 -5
  67. package/runtime/python/okstra_ctl/user_response.py +67 -2
  68. package/runtime/python/okstra_ctl/wizard.py +283 -3
  69. package/runtime/python/okstra_token_usage/report.py +11 -0
  70. package/runtime/schemas/final-report-v1.0.schema.json +336 -0
  71. package/runtime/skills/_fragments/bash-invocation-rule.md +1 -0
  72. package/runtime/skills/_fragments/preflight-outdated-cli.md +1 -0
  73. package/runtime/skills/_fragments/python-bootstrap-note.md +1 -0
  74. package/runtime/skills/okstra-brief-gen/SKILL.md +117 -122
  75. package/runtime/skills/okstra-container-build/SKILL.md +24 -14
  76. package/runtime/skills/okstra-graphify/SKILL.md +12 -4
  77. package/runtime/skills/okstra-inspect/SKILL.md +105 -99
  78. package/runtime/skills/okstra-manager/SKILL.md +1 -1
  79. package/runtime/skills/okstra-memory/SKILL.md +3 -3
  80. package/runtime/skills/okstra-rollup/SKILL.md +12 -6
  81. package/runtime/skills/okstra-run/SKILL.md +49 -88
  82. package/runtime/skills/{okstra-schedule → okstra-schedule-gen}/SKILL.md +38 -32
  83. package/runtime/skills/okstra-setup/SKILL.md +1 -1
  84. package/runtime/skills/okstra-setup/references/project-config.md +17 -16
  85. package/runtime/skills/okstra-usage/SKILL.md +5 -2
  86. package/runtime/skills/okstra-user-response/SKILL.md +23 -9
  87. package/runtime/templates/prd/brief.template.md +92 -92
  88. package/runtime/templates/reports/error-analysis-input.template.md +1 -1
  89. package/runtime/templates/reports/fan-out-unit.template.md +6 -6
  90. package/runtime/templates/reports/final-report.template.md +67 -0
  91. package/runtime/templates/reports/final-verification-input.template.md +6 -6
  92. package/runtime/templates/reports/i18n/en.json +31 -0
  93. package/runtime/templates/reports/i18n/ko.json +31 -0
  94. package/runtime/templates/reports/implementation-input.template.md +1 -1
  95. package/runtime/templates/reports/implementation-planning-input.template.md +1 -1
  96. package/runtime/templates/reports/improvement-discovery-input.template.md +1 -1
  97. package/runtime/templates/reports/quick-input.template.md +1 -1
  98. package/runtime/templates/reports/release-handoff-input.template.md +1 -1
  99. package/runtime/templates/reports/schedule.template.md +22 -22
  100. package/runtime/templates/reports/task-brief.template.md +3 -3
  101. package/runtime/templates/reports/user-response.template.md +20 -20
  102. package/runtime/templates/worker-prompt-preamble.md +111 -13
  103. package/runtime/validators/validate-run.py +426 -5
  104. package/runtime/validators/validate-schedule.py +5 -5
  105. package/src/cli-registry.mjs +7 -0
  106. package/src/commands/inspect/design-prep.mjs +23 -0
  107. package/src/lib/skill-catalog.mjs +2 -1
  108. package/docs/for-ai/skills/okstra-schedule.md +0 -320
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: okstra-run
3
- description: Use when the user wants to start an okstra task (cross-verification run) directly from the current Claude Code session — without spawning a new claude process. Equivalent in effect to `okstra.sh --task-type ...` but driven through interactive prompts. Trigger words include "okstra run", "okstra start", "start okstra", "begin okstra task", "run okstra in this session", "okstra here", "이어서 진행", "다음 단계 실행", "이 세션에서 okstra 시작".
3
+ description: Use when the user wants to start an okstra task (cross-verification run) directly from the current Claude Code session — without spawning a new claude process. Equivalent in effect to `okstra.sh --task-type ...` but driven through interactive prompts. Trigger words include "okstra run", "okstra start", "start okstra", "begin okstra task", "run okstra in this session", "okstra here", "continue on", "run the next phase", "start okstra in this session".
4
4
  ---
5
5
 
6
6
  # OKSTRA Run (in-session)
@@ -32,7 +32,7 @@ Every wizard call returns JSON. The two shapes you'll see:
32
32
  "progress": { "index": 5, "total": 11, "remaining": 6 } } }
33
33
  ```
34
34
 
35
- Every non-terminal `next` carries a `progress` object (`done` / `aborted` omit it). It holds `index` (1-based number of the **current screen**; pick_group counts as one screen), `total` (the wizard's forward estimate of the full screen count — may grow by a few when the user opens a branch, e.g. choosing *Customize* adds the model screen), `remaining` (`total − index`), and **`label`** — the ready-to-render Korean marker the wizard already composed (e.g. `Step 10/11 · 앞으로 1 스텝 남음`, or `Step 11/11 · 마지막 단계` on the final screen). **Always suffix the rendered prompt with `progress.label` verbatim** — see Step 3. Do not recompute the marker or decide "마지막 단계" yourself; only the wizard knows whether more screens follow.
35
+ Every non-terminal `next` carries a `progress` object (`done` / `aborted` omit it). It holds `index` (1-based number of the **current screen**; pick_group counts as one screen), `total` (the wizard's forward estimate of the full screen count — may grow by a few when the user opens a branch, e.g. choosing *Customize* adds the model screen), `remaining` (`total − index`), and **`label`** — the ready-to-render marker the wizard already composed (e.g. `Step 10/11 · 1 step remaining`, or `Step 11/11 · final step` on the final screen). **Always suffix the rendered prompt with `progress.label` verbatim** — see Step 3. Do not recompute the marker or decide "final step" yourself; only the wizard knows whether more screens follow.
36
36
 
37
37
  ```json
38
38
  { "ok": false, "error": "approved plan has no APPROVED marker: ...",
@@ -48,11 +48,11 @@ The wizard tells you *which UI to use* via `kind` (and the optional `multi` flag
48
48
  - `kind: "pick_group"` → render a SINGLE `AskUserQuestion` whose questions array maps 1:1 to the wizard's `questions[]`. For each entry use `questions[].label`, `questions[].options[].label`, and `multiSelect: questions[].multi`. Collect the user's chosen `options[].value` per tab, build a JSON object keyed by each `questions[].step`, and submit it as a single literal `--answer '{"lead_model":"opus","claude_model":"default",...}'`. A tab the user leaves at its default still gets its `"default"`/`""` value in the JSON. Never split a `pick_group` into multiple `AskUserQuestion` calls — the wizard already capped it at 4 tabs and emits any remainder as the next prompt.
49
49
  - `kind: "text"` → write `label` as a plain text message and consume the user's NEXT message as the answer.
50
50
  - `kind: "done"` → input collection finished; move to Step 5.
51
- - `kind: "aborted"` → the user picked 중단; the wizard is terminally cancelled. Tell the user on one short line that the run setup was aborted, delete the state file (`rm` with the literal path), and stop this skill — do NOT call `render-args` or `render-bundle` (the wizard rejects `render-args` on an aborted state).
51
+ - `kind: "aborted"` → the user picked abort; the wizard is terminally cancelled. Tell the user on one short line that the run setup was aborted, delete the state file (`rm` with the literal path), and stop this skill — do NOT call `render-args` or `render-bundle` (the wizard rejects `render-args` on an aborted state).
52
52
 
53
- The final `confirm` step is a normal `pick` step with three options — `Proceed` / `Edit` / `중단`(abort) — and is rendered the same way (no special handling). `Edit` rewinds to any earlier step (including `base-ref`); `중단` terminally cancels the wizard. The branch/worktree decision the run will actually use (for `implementation`, the **stage worktree** — not the task-key directory) is folded into the Step 4 confirmation summary block as a `worktree` line, so there is no separate branch-confirm prompt.
53
+ The final `confirm` step is a normal `pick` step with three options — `Proceed` / `Edit` / `Abort`(abort) — and is rendered the same way (no special handling). `Edit` rewinds to any earlier step (including `base-ref`); `Abort` terminally cancels the wizard. The branch/worktree decision the run will actually use (for `implementation`, the **stage worktree** — not the task-key directory) is folded into the Step 4 confirmation summary block as a `worktree` line, so there is no separate branch-confirm prompt.
54
54
 
55
- Never invent additional questions. Never reorder. **Never drop, hide, or merge a `pick` / `pick_group` option** — render every `options[]` entry as its own selectable `AskUserQuestion` choice, including entries that carry a `(default)` / `(recommended)` suffix. Do NOT collapse a multi-option pick into a "recommended + 직접 입력 / Other" shortlist: the wizard's `options[]` array IS the complete, authoritative choice set. Example: if a pick's `options[]` carries N entries, render all N as selectable choices — never abbreviate a multi-option step down to one recommended value. The run-prompt recommendation rule (1–2 추천 + 직접 입력) applies ONLY to prompts this skill authors itself (e.g. the conformance-waiver picker), never to wizard-provided `options[]`. Never use `AskUserQuestion` for `text` prompts — the wizard explicitly chose `text` to avoid the picker-Other re-render lag.
55
+ Never invent additional questions. Never reorder. **Never drop, hide, or merge a `pick` / `pick_group` option** — render every `options[]` entry as its own selectable `AskUserQuestion` choice, including entries that carry a `(default)` / `(recommended)` suffix. Do NOT collapse a multi-option pick into a "recommended + Enter directly / Other" shortlist: the wizard's `options[]` array IS the complete, authoritative choice set. Example: if a pick's `options[]` carries N entries, render all N as selectable choices — never abbreviate a multi-option step down to one recommended value. The run-prompt recommendation rule (1–2 recommendations + Enter directly) applies ONLY to prompts this skill authors itself (e.g. the conformance-waiver picker), never to wizard-provided `options[]`. Never use `AskUserQuestion` for `text` prompts — the wizard explicitly chose `text` to avoid the picker-Other re-render lag.
56
56
 
57
57
  ## Step 1: Preflight
58
58
 
@@ -89,7 +89,7 @@ Output: the same `{ok, next}` JSON described above. The first `next` is always `
89
89
 
90
90
  Repeat until `next.kind == "done"` (or `"aborted"` — terminal cancel, see "How the wizard talks to you"):
91
91
 
92
- 1. **Render** the prompt according to `kind` (and `multi` for pick). **Always append the progress marker to the rendered question label** (the `AskUserQuestion` question text, or the `text`-prompt message): suffix it with ` (<next.progress.label>)` — render `progress.label` exactly as the wizard sent it, never recompute it. Example: label `Step 8/11 · 앞으로 3 스텝 남음` → `모델을 선택하세요 (Step 8/11 · 앞으로 3 스텝 남음)`. Re-prompts after `ok: false` reuse `current.progress.label` the same way. The progress marker is presentation-only — never send it back to the wizard as part of an answer.
92
+ 1. **Render** the prompt according to `kind` (and `multi` for pick). **Always append the progress marker to the rendered question label** (the `AskUserQuestion` question text, or the `text`-prompt message): suffix it with ` (<next.progress.label>)` — render `progress.label` exactly as the wizard sent it, never recompute it. Example: label `Step 8/11 · 3 steps remaining` → `Select a model (Step 8/11 · 3 steps remaining)`. Re-prompts after `ok: false` reuse `current.progress.label` the same way. The progress marker is presentation-only — never send it back to the wizard as part of an answer.
93
93
  - `pick` + `multi: false` → `AskUserQuestion` with `multiSelect: false`, `label`, and `options`. The user's chosen option's `value` is the answer string.
94
94
  - `pick` + `multi: true` → `AskUserQuestion` with `multiSelect: true`, `label`, and `options`. Join the selected `value`s with `,` into a single literal CSV string (e.g. `"claude,codex,antigravity"`) and submit it as a single `--answer "claude,codex,antigravity"`. Empty selection submits `--answer ""` and the wizard re-prompts.
95
95
  - `pick_group` → one `AskUserQuestion` with one question per `questions[]` entry (tab). Map each tab's selected `value` back by `questions[].step`, assemble a JSON object, and submit it as a single literal `--answer '<json>'`.
@@ -113,14 +113,14 @@ Repeat until `next.kind == "done"` (or `"aborted"` — terminal cancel, see "How
113
113
 
114
114
  That is the entire interactive flow. The wizard handles:
115
115
 
116
- - new-vs-existing task split (남은 작업 — `workStatus != done` — 최신순 3개 추천 + 직접 입력), task-group / task-id slug validation (task-group 은 최근 task 사용 + 최근 `.okstra/briefs/<group>/` 생성 활동을 합산한 최신 후보 3개 추천 + 직접 입력, task-id 는 같은 group 의 최근 후보 3개 추천 + 직접 입력),
117
- - task-type pick (추천 3개 — `nextRecommendedPhase` recommended / 현재 phase 재실행 / 라이프사이클 다음 단계 — + 직접 입력; 직접 입력은 후속 `text` 단계에서 전체 task-type 화이트리스트로 검증),
118
- - brief path — **entry task-type(requirements-discovery / error-analysis / improvement-discovery)에서만 질문** (same-group `.okstra/briefs/<task-group>/**/*.md` candidates first, sorted by the newer of file-created/modified time and latest task-catalog use; direct input last; `유지 / 변경` for existing entry tasks). downstream task-type 은 manifest 의 brief 를 자동 carry-in 하고, 등록 brief 가 없으면 `brief_carry` 3-옵션(entry 전환 추천 / 직접 입력 / 중단)이 뜬다. `release-handoff` 는 brief 단계가 아예 없다 — prepare 가 검증 보고서 인용 input 문서를 생성한다,
116
+ - new-vs-existing task split (remaining work — `workStatus != done` — top-3 newest recommendations + Enter directly), task-group / task-id slug validation (task-group offers the top-3 newest candidates combining recent task use + recent `.okstra/briefs/<group>/` creation activity + Enter directly; task-id offers the top-3 recent candidates from the same group + Enter directly),
117
+ - task-type pick (3 recommendations — `nextRecommendedPhase` recommended / re-run the current phase / the lifecycle's next step — + Enter directly; Enter directly is validated against the full task-type whitelist in a follow-up `text` step),
118
+ - brief path — **asked only for entry task-types (requirements-discovery / error-analysis / improvement-discovery)** (same-group `.okstra/briefs/<task-group>/**/*.md` candidates first, sorted by the newer of file-created/modified time and latest task-catalog use; direct input last; `Keep / Change` for existing entry tasks). A downstream task-type auto carries in the manifest's brief, and when no registered brief exists a `brief_carry` 3-option prompt appears (recommend switching to entry / Enter directly / Abort). `release-handoff` has no brief step at all — prepare generates the input document that cites the verification report,
119
119
  - base-ref pick + git rev-parse validation (skipped when reusing an active worktree),
120
- - `implementation`-only sub-flow: approved-plan path (frontmatter `approved: true` check) + stage pick (`auto` = 의존성 충족된 가장 빠른 미완료 stage, 또는 특정 stage 번호) + executor pick. approved-plan 선택 시 그 run 의 sibling `user-responses/` 에서 plan 과 source-report·seq 가 일치하는, 보고서에서 내보낸 `## APPROVAL` sidecar 를 감지하면 approve-confirm 단계가 3-옵션(`yes_apply` 내보낸 기록대로 승인+옵션 적용 추천 / `yes` 승인만 / `no` 중단)으로 확장된다 — `yes_apply` 는 옵션을 plan 의 `optionCandidates` 에 대해 검증한 뒤 기존 승인·옵션 경로로 적용한다,
121
- - `release-handoff`-only sub-flow: approved plan 자동 해소 후 `handoff_stage_pick` 멀티선택 — eligible stage 묶음(stage-group) 또는 전체 task(accepted whole-task 검증 보고서 존재 시) 선택; 결과는 render-args 의 `stages` 키(csv, whole-task 면 빈 값)로 나간다,
120
+ - `implementation`-only sub-flow: approved-plan path (frontmatter `approved: true` check) + stage pick (`auto` = the earliest incomplete stage whose dependencies are satisfied, or a specific stage number) + executor pick. When an approved plan is selected and a `## APPROVAL` sidecar exported from the report — matching the plan on source-report·seq — is detected in that run's sibling `user-responses/`, the approve-confirm step expands to 3 options (`yes_apply` recommended: approve + apply the option as exported / `yes` approve only / `no` abort) — `yes_apply` validates the option against the plan's `optionCandidates` before applying it via the existing approval·option path,
121
+ - `release-handoff`-only sub-flow: after the approved plan auto-resolves, a `handoff_stage_pick` multi-select — choose an eligible stage bundle (stage-group) or the whole task (when an accepted whole-task verification report exists); the result goes out as render-args' `stages` key (csv, empty when whole-task),
122
122
  - `Use defaults / Customize` branch with profile-aware worker/model questions,
123
- - **resume-clarification (in-session 등가)** — 셸의 `okstra.sh --resume-clarification` 에 대응하는 별도 모드나 플래그는 없고, 표준 흐름의 두 단계가 그 실질을 수행한다. (1) `reuse_previous` (직전 run 설정 재사용 예/아니오 — `requirements-discovery` / `error-analysis` / `implementation-planning` 에서, 직전 run-inputs 가 있을 때만): YES 면 워커·모델·directive·related-tasks 를 한 번에 prefill 한다. (2) `clarification_pick`: 재실행하는 **task-type 자신의** 직전 `final-report` 가 있으면 그것을 carry-in 입력으로 자동 추천하고(없으면 전체 phase 중 mtime 최신으로 폴백), 같은 run 의 `user-responses/` 사이드카(사용자가 채운 답변)를 함께 첨부한다. 선택된 경로는 prepare 의 `--clarification-response` 로 전달된다 — 사용자는 보고서의 `Export user response` 로 사이드카를 만들어 `runs/<task-type>/user-responses/` 에 둔 뒤 같은 phase 를 다시 실행하면 된다,
123
+ - **resume-clarification (in-session equivalent)** — there is no separate mode or flag matching the shell's `okstra.sh --resume-clarification`; two steps of the standard flow carry out its substance. (1) `reuse_previous` (yes/no to reuse the previous run's settings — in `requirements-discovery` / `error-analysis` / `implementation-planning`, only when prior run-inputs exist): YES prefills workers·model·directive·related-tasks at once. (2) `clarification_pick`: if the **task-type's own** previous `final-report` exists it is auto-recommended as the carry-in input (falling back to the newest by mtime across all phases when absent), and the same run's `user-responses/` sidecar (answers the user filled in) is attached alongside. The chosen path is passed to prepare as `--clarification-response` — the user makes the sidecar via the report's `Export user response`, places it in `runs/<task-type>/user-responses/`, and re-runs the same phase,
124
124
  - `release-handoff` PR template override + persist scope,
125
125
  - final `Proceed / Edit` confirmation; on `Edit` the wizard asks which step to rewind to and clears every later answer.
126
126
 
@@ -134,7 +134,7 @@ When `next.step == "confirm"`, before relaying the picker, fetch the human-reada
134
134
  okstra wizard confirmation --state-file /var/folders/.../okstra-wizard.AbCd.json
135
135
  ```
136
136
 
137
- Output: `{ok: true, text: "선택 확인:\n task-type : ...\n ..."}`. Print `text` to the user, then render the `confirm` picker (Proceed / Edit).
137
+ Output: `{ok: true, text: "Selection summary:\n task-type : ...\n ..."}`. Print `text` to the user, then render the `confirm` picker (Proceed / Edit).
138
138
 
139
139
  ## Step 5: Render the task bundle
140
140
 
@@ -195,7 +195,7 @@ okstra render-bundle \
195
195
  --fix-cycle "<args.fix-cycle>"
196
196
  ```
197
197
 
198
- `render-bundle` auto-supplies `--workspace-root` and forces `--render-only`. Stdout prints `okstra task root:`, `okstra instruction-set:`, and the full rendered lead prompt. Parse the labelled lines for `TASK_ROOT` and `INSTRUCTION_SET_PATH`. Also watch for an optional `okstra concurrent-run stages:` label line — present only when a concurrent run is detected (see "동시-run 감지 분기" below).
198
+ `render-bundle` auto-supplies `--workspace-root` and forces `--render-only`. Stdout prints `okstra task root:`, `okstra instruction-set:`, and the full rendered lead prompt. Parse the labelled lines for `TASK_ROOT` and `INSTRUCTION_SET_PATH`. Also watch for an optional `okstra concurrent-run stages:` label line — present only when a concurrent run is detected (see "Concurrent-run detection branch" below).
199
199
 
200
200
  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`.
201
201
 
@@ -209,48 +209,30 @@ This is **never** a lead/worker self-exemption — only the user may waive. Offe
209
209
 
210
210
  1. (recommended) Run the conformance script — no waiver.
211
211
  2. Waive this stage — ask the user for the exact `<stageKey>` and reason, then pass `--qa-waiver "<stageKey>:<reason>"` to `render-bundle` (reason = the user's words, unedited).
212
- 3. 직접 입력 — the user types the full `<stageKey>:<reason>` value.
212
+ 3. Enter directly — the user types the full `<stageKey>:<reason>` value.
213
213
 
214
214
  When the user picks a waiver, append `--qa-waiver "<stageKey>:<reason>"` to the `render-bundle` invocation above. Omit the flag entirely otherwise (do **not** pass `--qa-waiver ""`). A malformed value or unknown `<stageKey>` aborts `render-bundle` with a `PrepareError`.
215
215
 
216
- ### 동시-run 감지 분기 (concurrent-run)
216
+ ### Concurrent-run detection branch (concurrent-run)
217
217
 
218
- `render-bundle` stdout 에 `okstra concurrent-run stages: <stages>` 라벨 라인이
219
- 있으면(같은 task-key 의 다른 implementation run 이 `<stages>` 를 점유 중), launch
220
- 프롬프트는 이미 "Concurrent-run marker" 게이트로 렌더돼 있다. 이 라인이
221
- 없으면 동시-run 이 아니므로 이 분기를 건너뛴다. 라인이 있으면 dispatch 전에
222
- 사용자에게 3-옵션 recommendation picker 를 제시한다 (run-prompt recommendation 규칙:
223
- 1–2 추천 + 직접 입력; 이 picker 는 스킬이 author 하는 것이라 wizard `options[]`
224
- 제약과 무관):
218
+ If `render-bundle` stdout carries an `okstra concurrent-run stages: <stages>` label line (another implementation run on the same task-key is occupying `<stages>`), the launch prompt has already been rendered with the "Concurrent-run marker" gate. If this line is absent it is not a concurrent run, so skip this branch. If present, before dispatch present a 3-option recommendation picker to the user (run-prompt recommendation rule: 1–2 recommendations + Enter directly; this picker is authored by the skill, so it is unconstrained by the wizard `options[]` rule):
225
219
 
226
- 1. (추천) 이대로 진행 — 이미 렌더된 bundle 을 그대로 사용한다. 각 세션은 자기
227
- implicit team 을 쓰므로 동시 run 끼리 team 충돌이 없고, split-pane 도 정상 동작한다.
228
- 2. 대기 — 지금 dispatch 를 보류한다. stage worktree·run-context 는 보존되므로,
229
- 점유 중인 다른 run 종료 후 같은 stage 를 resume 으로 재개하면 그때는 정상 team
230
- 경로다. resume 명령(`okstra-inspect` history → resume)을 사용자에게 출력한다.
231
- 3. 직접 입력.
220
+ 1. (recommended) Proceed as-is — use the already-rendered bundle. Each session uses its own implicit team, so concurrent runs have no team conflict and split-pane works fine.
221
+ 2. Wait — hold the dispatch for now. The stage worktree·run-context are preserved, so after the other occupying run finishes, resuming the same stage takes the normal team path. Print the resume command (`okstra-inspect` history → resume) to the user.
222
+ 3. Enter directly.
232
223
 
233
224
  ### Stale git SHA recovery (git-reconcile gate)
234
225
 
235
- `render-bundle` 이 `Recorded stage SHAs no longer match the git history` 를 포함한
236
- `PrepareError` 로 실패하면, okstra 밖에서 git 히스토리가 바뀐 것이다(rebase /
237
- squash / 리뷰 반영 amend / branch 삭제). 절대 registry/consumers 를 손으로
238
- 고치지 말고 다음 순서로 회복한다:
239
-
240
- 1. 에러 메시지에 인쇄된 `okstra git-reconcile … --check --json` 명령을 그대로 실행해
241
- stale 리포트를 얻는다. (patch-id 로 내용 동일성이 증명되는 항목은 prepare
242
- 가 이미 자동 화해했으므로, 여기 남는 것은 confirm 항목뿐이다.)
243
- 2. confirm 항목별로 사용자에게 3-옵션 picker 를 제시한다:
244
- - **`stage-<N>` branch 의 현재 tip 으로 재기록 (추천)** — 리뷰 반영 등
245
- 의도된 수정이 그 branch 에 있을 때.
246
- - **다른 ref 직접 입력** — 사용자가 commit/branch/tag 를 직접 지정.
247
- - **중단** — 회복하지 않고 run 을 멈춘다.
248
- 3. 선택된 ref 로 `okstra git-reconcile … --apply --stage <N> --use-ref <ref>`
249
- 를 실행한 뒤, 실패했던 `render-bundle` 을 동일 인자로 재시도한다.
250
-
251
- anchor(`implementation_base_commit`)가 unresolvable 로 보고되면 같은 명령의
252
- `--reset-anchor <ref>` 를 사용자 확인 후 실행한다. picker 없이 confirm 항목을
253
- 보정하는 것은 금지 — 런타임도 `--use-ref` 없는 confirm 보정을 거부한다.
226
+ If `render-bundle` fails with a `PrepareError` containing `Recorded stage SHAs no longer match the git history`, the git history changed outside okstra (rebase / squash / review-feedback amend / branch deletion). Never fix the registry/consumers by hand; recover in this order:
227
+
228
+ 1. Run the `okstra git-reconcile … --check --json` command printed in the error message verbatim to get the stale report. (Items whose content-identity is proven by patch-id were already auto-reconciled by prepare, so only confirm items remain here.)
229
+ 2. For each confirm item, present a 3-option picker to the user:
230
+ - **Re-record to the `stage-<N>` branch's current tip (recommended)** — when an intended change such as review feedback lives on that branch.
231
+ - **Enter a different ref directly** — the user names a commit/branch/tag.
232
+ - **Abort** — stop the run without recovering.
233
+ 3. Run `okstra git-reconcile … --apply --stage <N> --use-ref <ref>` with the chosen ref, then retry the failed `render-bundle` with the same arguments.
234
+
235
+ If the anchor (`implementation_base_commit`) is reported unresolvable, run the same command's `--reset-anchor <ref>` after user confirmation. Correcting a confirm item without the picker is forbidden — the runtime also rejects a confirm correction without `--use-ref`.
254
236
 
255
237
  ## Step 6: Take over as Claude lead
256
238
 
@@ -261,45 +243,24 @@ Then proceed through the phases exactly as the lead prompt directs (Phase 1 cont
261
243
  Inform the user with one short line:
262
244
  > Took over as Claude lead for `<taskKey>` (`<task-type>`). Run dir: `<RUN_DIR_RELATIVE_PATH>`. Beginning Phase 1 (context loading).
263
245
 
264
- ## Step 7: implementation 무인 연쇄 (chain-stages)
265
-
266
- `task-type == implementation` 이고 Step 5 render-args 의 `chain-stages` CSV 가 원소 2개
267
- 이상이면, 현재 세션이 오케스트레이터로서 stage 를 의존성 순서대로 무인 연쇄
268
- 실행한다(단일 원소면 기존 단일 run 과 동일하므로 이 절은 건너뛴다 — Step 6 종료가 곧
269
- run 종료다).
270
-
271
- 큐 = `chain-stages` 를 `,` 로 분해한 위상정렬 stage 리스트(Task 5 가 의존성
272
- closure 를 위상정렬해 내보낸 순서). 큐의 각 stage `N` 에 대해 순서대로:
273
-
274
- 1. Step 5 의 `render-bundle` 을 동일 인자로, 단 `--stage N` 으로 호출한다(base
275
- commit 은 prepare 가 predecessor 의 done `head_commit` 으로 자동 계산하므로 손으로
276
- 넘기지 않는다). Step 5 의 conformance waiver offer·동시-run 감지·git-reconcile
277
- 게이트는 매 stage 의 `render-bundle` 마다 동일하게 적용된다.
278
- 2. Step 6 대로 Claude lead 가 되어 그 stage 의 Phase 1~7 을 인라인 실행한다. Phase 6
279
- 의 lead post-stage persistence 가 `runs/<plan-task-key>/consumers.jsonl` 에 그
280
- stage 의 `status:"done"` 행을 append 한다(implementation 프로파일 지시).
281
- 3. 그 `done` 행이 기록됐는지 확인한 뒤 다음 stage 로 넘어간다. 매 stage 경계에서
282
- 컨텍스트를 정리한다(이전 배치의 잔여 pane·완료 teammate).
283
- 4. 각 stage 시작/완료마다 한 줄 보고: `stage N/<총개수> 시작` / `stage N done → 다음 K`.
284
-
285
- 큐를 모두 소화하면 연쇄를 종료하고 사용자에게 완료를 보고한다.
286
-
287
- ### 다음 stage 가 아직 ready 아님 — 정상 종료 (예외 게이트 아님)
288
- 연쇄 큐에는 의존성 closure 때문에 **다른 implementation run 이 started/reserved 로
289
- 점유한 stage 가 포함될 수 있다.** 그 stage 의 `render-bundle` 은
290
- `--stage N already in progress or reserved by another run`(StageTargetError)으로
291
- 거부된다. 이건 사람 판단이 필요한 예외 게이트가 **아니라** "다음 stage 가 아직
292
- ready 아님" 상황이다. 이 거부를 만나면 연쇄를 **정상 종료**하고 남은 큐를
293
- 사용자에게 보고한다(예: `남은 큐: stage 4, 5 — 점유 해제 후 okstra-run 으로 재개`).
294
- 아래 예외 게이트(데이터 손상·동시 점유 충돌 확인)와는 다른 분기다.
295
-
296
- ### 연쇄 중 예외 게이트
297
- `render-bundle` 이 Step 5 의 동시-run 충돌 감지(concurrent-run 분기)나 git
298
- stale-SHA 재조정(git-reconcile 분기)을 띄우면, **그 stage 에서 연쇄를 멈추고**
299
- 게이트를 Step 5 의 절차대로 사용자에게 그대로 제시한다. 사용자가 게이트를
300
- 해소하면 그 자리에서 연쇄를 재개한다(남은 큐를 이어서 처리). 데이터 손상·동시
301
- 점유 충돌은 사람이 확인한다 — 이것이 무인 연쇄의 안전 경계다. (위 "ready 아님"
302
- 거부와 달리, 이 두 분기는 큐를 버리지 않고 사용자 해소를 기다린다.)
246
+ ## Step 7: implementation unattended chaining (chain-stages)
247
+
248
+ When `task-type == implementation` and Step 5 render-args' `chain-stages` CSV has 2+ elements, the current session acts as the orchestrator and runs the stages in dependency order as an unattended chain (a single element behaves like the existing single run, so skip this section — the end of Step 6 is the end of the run).
249
+
250
+ Queue = the topologically-sorted stage list from splitting `chain-stages` on `,` (the order Task 5 emitted by topologically sorting the dependency closure). For each stage `N` in the queue, in order:
251
+
252
+ 1. Call Step 5's `render-bundle` with the same arguments but `--stage N` (the base commit is auto-computed by prepare from the predecessor's done `head_commit`, so do not pass it by hand). Step 5's conformance waiver offer·concurrent-run detection·git-reconcile gates apply identically to each stage's `render-bundle`.
253
+ 2. As in Step 6, become Claude lead and run that stage's Phase 1–7 inline. Phase 6's lead post-stage persistence appends that stage's `status:"done"` row to `runs/<plan-task-key>/consumers.jsonl` (per the implementation profile directive).
254
+ 3. After confirming that `done` row was written, move to the next stage. Clean up context at each stage boundary (leftover panes·finished teammates from the previous batch).
255
+ 4. One-line report at each stage start/finish: `stage N/<total> start` / `stage N done → next K`.
256
+
257
+ Once the whole queue is consumed, end the chain and report completion to the user.
258
+
259
+ ### Next stage not yet ready — normal termination (not an exception gate)
260
+ Because of the dependency closure, the chain queue **may include a stage that another implementation run has occupied as started/reserved.** That stage's `render-bundle` is rejected with `--stage N already in progress or reserved by another run` (StageTargetError). This is **not** an exception gate needing human judgment but a "next stage not yet ready" situation. On this rejection, **terminate the chain normally** and report the remaining queue to the user (e.g. `remaining queue: stage 4, 5 — resume with okstra-run after occupancy is released`). This is a different branch from the exception gate below (data corruption·concurrent-occupancy conflict confirmation).
261
+
262
+ ### Exception gate during chaining
263
+ If `render-bundle` raises Step 5's concurrent-run conflict detection (concurrent-run branch) or git stale-SHA reconciliation (git-reconcile branch), **stop the chain at that stage** and present the gate to the user exactly as Step 5 prescribes. Once the user resolves the gate, resume the chain in place (continue with the remaining queue). Data corruption·concurrent-occupancy conflicts are confirmed by a human — this is the safety boundary of unattended chaining. (Unlike the "not ready" rejection above, these two branches do not discard the queue; they wait for user resolution.)
303
264
 
304
265
  ## Persisting the PR template scope (release-handoff)
305
266
 
@@ -325,4 +286,4 @@ Do not read the wizard state file directly. `okstra wizard outcome` exposes any
325
286
 
326
287
  - Echo each captured answer (`result.echo`) on one short line so the user sees what was registered.
327
288
  - Never invent identity; if a `text` prompt returns an empty answer where the wizard rejects it, the user must retry.
328
- - After Step 6, begin the lead workflow without re-summarizing the skill itself. 단일 run 은 Step 6 종료가 곧 run 종료다 — 단, `chain-stages` 가 원소 2개 이상인 무인 연쇄에서는 Step 7 의 큐가 빌 때까지(또는 "ready 아님"/예외 게이트로 멈출 때까지) Step 6 을 stage 마다 반복한 뒤 종료한다.
289
+ - After Step 6, begin the lead workflow without re-summarizing the skill itself. For a single run, the end of Step 6 is the end of the run — but in an unattended chain where `chain-stages` has 2+ elements, repeat Step 6 per stage until Step 7's queue is empty (or it stops at a "not ready" / exception gate), then finish.
@@ -1,16 +1,16 @@
1
1
  ---
2
- name: okstra-schedule
3
- description: Use when the user asks for a task-group work schedule, a consolidated implementation plan across multiple tasks in a task-group, or wants to generate a "schedule" / "일정" / "작업 계획표" for non-done tasks. Trigger words include "okstra schedule", "<task-group> 일정 만들어", "<task-group> schedule 생성", "task-group 작업 계획".
2
+ name: okstra-schedule-gen
3
+ description: Use when the user asks for a task-group work schedule, a consolidated implementation plan across multiple tasks in a task-group, or wants to generate a "schedule" / "work plan" for non-done tasks. Trigger words include "okstra schedule", "make a schedule for <task-group>", "generate a <task-group> schedule", "task-group work plan".
4
4
  model: opus
5
5
  ---
6
6
 
7
- # OKSTRA Schedule
7
+ # OKSTRA Schedule Gen
8
8
 
9
9
  Generate a consolidated work schedule for the selected `implementation-planning` stages of every non-done task in a given `task-group` (or a single `task-id`). For each task the skill reads its `implementation-planning` final-report **Stage Map** as the authoritative forward-looking decomposition, asks the user which stages to include, drafts a single Markdown plan, and only writes the final file after an **independent verifier subagent** confirms the draft covers exactly the selected stages. It runs as a **lead + verifier** flow; the frontmatter `model: opus` switches supporting harnesses to Opus-class for the turn — stage-level cross-task synthesis needs that reasoning depth.
10
10
 
11
11
  ## When to Use
12
12
 
13
- - User asks to generate a work schedule / plan / "일정" for an entire `task-group`
13
+ - User asks to generate a work schedule / plan / "schedule" for an entire `task-group`
14
14
  - User wants a single document that summarizes all non-done tasks with effort, risk, and dependencies
15
15
 
16
16
  **Do NOT use** for single-task analysis (use `okstra-inspect status`) or to execute one task (use `okstra-run`).
@@ -19,37 +19,43 @@ Explicit command form: `okstra schedule <task-group> [--title "<custom title>"]
19
19
 
20
20
  ## Step 0: Preflight
21
21
 
22
+ <!-- BEGIN FRAGMENT: bash-invocation-rule -->
22
23
  Run one Bash tool call, starting with the literal token `okstra` (never wrapped in `if`/`eval`/`export`/`$(...)`/`VAR=...`/`||`/`&&`/`npx` — a non-literal leading token defeats the `Bash(okstra:*)` permission match):
24
+ <!-- END FRAGMENT: bash-invocation-rule -->
23
25
 
24
26
  ```bash
25
27
  okstra preflight --runtime claude-code --json
26
28
  ```
27
29
 
28
- Parse the stdout JSON. `ok: true` → carry `projectRoot` as a literal string and use it to locate `.okstra/discovery/task-catalog.json` and the task-group directory. `ok: false` → tell the user to run `/okstra-setup` first, then stop. If the call fails with `unknown command: preflight`, the `okstra` binary on PATH predates this skill — tell the user to update it (`npm i -g okstra@latest`), then stop (`/okstra-setup` does not update the binary).
30
+ Parse the stdout JSON. `ok: true` → carry `projectRoot` as a literal string and use it to locate `.okstra/discovery/task-catalog.json` and the task-group directory. `ok: false` → tell the user to run `/okstra-setup` first, then stop.
31
+
32
+ <!-- BEGIN FRAGMENT: preflight-outdated-cli -->
33
+ If the call fails with `unknown command: preflight`, the `okstra` binary on PATH predates this skill — tell the user to update it (`npm i -g okstra@latest`), then stop (`/okstra-setup` does not update the binary).
34
+ <!-- END FRAGMENT: preflight-outdated-cli -->
29
35
 
30
36
  ## Audience & authority (READ FIRST — drives everything below)
31
37
 
32
- **The schedule is a client-facing work plan.** It assumes the team has all permissions and can proceed without further approval. Even when the underlying per-task reports flag blocking items, missing approvals, or "사용자 확인 필요 항목", **the schedule MUST NOT surface them** — those belong in the internal report. Never emit a decision checklist, a `#### 사용자 확인 필요 항목` sub-section, `Done`/`Ready?`/`Blocking Decisions` columns, or checkbox lists; "Status" reflects work phase only.
38
+ **The schedule is a client-facing work plan.** It assumes the team has all permissions and can proceed without further approval. Even when the underlying per-task reports flag blocking items, missing approvals, or "items requiring user confirmation", **the schedule MUST NOT surface them** — those belong in the internal report. Never emit a decision checklist, a `#### Items requiring user confirmation` sub-section, `Done`/`Ready?`/`Blocking Decisions` columns, or checkbox lists; "Status" reflects work phase only.
33
39
 
34
40
  **Assume the user and their team hold full authority and every permission required.** External approvals, access grants, sign-off, and vendor coordination are treated as already satisfied unless a report names a concrete external dependency outside the user's control. Concretely:
35
41
 
36
42
  - **Effort sizing & day totals** count engineering work only — strip approval-waiting / coordination buffers from source sizings (note the adjustment in `## Executive Summary` if material).
37
43
  - **Gantt bars** represent engineering duration only; no dead-time gaps for approval cycles. `(after <TASK-ID>)` marks genuine engineering dependencies only.
38
44
  - **Risk Mitigation Strategy** lists real engineering risks (data loss, regression surface, rollback path) — permission/coordination items are dropped.
39
- - **Recommended Immediate Actions / Next Action** are concrete engineering steps; "권한 확인", "승인 요청", "이해관계자 정렬" 류는 출력 금지.
45
+ - **Recommended Immediate Actions / Next Action** are concrete engineering steps; items like "permission check", "approval request", "stakeholder alignment" MUST NOT be emitted.
40
46
  - **Cross-Task Dependencies** covers engineering coupling only (shared modules, release order, package versions).
41
47
 
42
- **The schedule must be self-contained.** Opaque codes pulled from internal reports (`FC-5`, `UC-12`, `M1`, decision-item letters, …) must not appear unresolved. Choose one per identifier: **Form A** (≤3 codes) — replace the code inline with a 5–20자 one-line description of the item; **Form B** (≥4 recurring codes) — keep the codes and emit a `## Glossary` table as the last section resolving every one. Decision-item letters (`A1`, `B2`, …) are approval items and may not appear at all. TASK-IDs listed in `## At a Glance` need neither.
48
+ **The schedule must be self-contained.** Opaque codes pulled from internal reports (`FC-5`, `UC-12`, `M1`, decision-item letters, …) must not appear unresolved. Choose one per identifier: **Form A** (≤3 codes) — replace the code inline with a 5–20 character one-line description of the item; **Form B** (≥4 recurring codes) — keep the codes and emit a `## Glossary` table as the last section resolving every one. Decision-item letters (`A1`, `B2`, …) are approval items and may not appear at all. TASK-IDs listed in `## At a Glance` need neither.
43
49
 
44
50
  ## Contract SSOT — template + validator
45
51
 
46
- The installed template `~/.okstra/templates/reports/schedule.template.md` is the **byte-for-byte SSOT** for the output shape: frontmatter, top header block, the mandatory `##` heading list and order, per-task `Item / Detail` field labels and sub-section order, table column shapes, the ASCII Gantt format (relative day axis, plain fence, `█`/`░`/`! crit`/`est` legend), the dependency-graph shapes, and the optional `## Glossary` gate. **Read the template before writing the schedule and follow it exactly** — do not re-derive section shapes from memory. Headings and field labels stay English literals regardless of the source-report language; body prose is Korean. When a section has no data, render its heading with `_없음_` — never delete or reorder headings. Never emit mermaid or any graph DSL.
52
+ The installed template `~/.okstra/templates/reports/schedule.template.md` is the **byte-for-byte SSOT** for the output shape: frontmatter, top header block, the mandatory `##` heading list and order, per-task `Item / Detail` field labels and sub-section order, table column shapes, the ASCII Gantt format (relative day axis, plain fence, `█`/`░`/`! crit`/`est` legend), the dependency-graph shapes, and the optional `## Glossary` gate. **Read the template before writing the schedule and follow it exactly** — do not re-derive section shapes from memory. Headings and field labels stay English literals regardless of the source-report language; body prose is Korean. When a section has no data, render its heading with `_none_` — never delete or reorder headings. Never emit mermaid or any graph DSL.
47
53
 
48
54
  `~/.okstra/lib/validators/validate-schedule.py` is the enforcement for all of the above (heading order, field labels, controlled vocabulary — e.g. `Med-High` is the canonical risk form — forbidden translations, checkbox bans, Gantt fence rules, unresolved-code detection). The Step 4 ambiguous-classification rationale line is the one rule the validator does not yet enforce — emit it yourself.
49
55
 
50
56
  One computation rule the template scaffold cannot carry inline:
51
57
 
52
- - **Effort-to-Day mapping**: day ranges per size are defined once in the template's `### Effort Sizing 기준` table. For the At a Glance totals line, sum that table's lower bounds across in-scope tasks for the lower total and upper bounds for the upper total.
58
+ - **Effort-to-Day mapping**: day ranges per size are defined once in the template's `### Effort Sizing Criteria` table. For the At a Glance totals line, sum that table's lower bounds across in-scope tasks for the lower total and upper bounds for the upper total.
53
59
 
54
60
  ## Procedure
55
61
 
@@ -58,10 +64,10 @@ One computation rule the template scaffold cannot carry inline:
58
64
  1. Read `.okstra/discovery/task-catalog.json`.
59
65
  2. **Resolve which task-group to schedule — never silently guess.**
60
66
  - The user **explicitly named a task-group** (as the `okstra schedule <task-group>` argument or unambiguously in the request) → use that token; skip the picker and go to sub-step 3.
61
- - The user **named no task-group, or the named token matches 0 or ≥2 groups** → present a 3-option picker via `AskUserQuestion` and do NOT proceed until the user chooses. Build the options from the catalog: walk `tasks[]` in catalog order (already `updatedAt` desc — see `scripts/okstra_ctl/render.py:654`), collect distinct `taskGroupPathSegment` values that have ≥1 entry whose resolved `workStatus` is **non-done** (defer to Step 2's inference table), and offer the newest **1–2** such groups as recommendations. The **last option is always `직접 입력`** (free-text group token, fed into sub-step 3). Label each recommendation with its non-done task count (e.g. `uploadFont (non-done 3)`).
62
- - If **zero groups have a non-done task** (or the catalog is empty), do NOT open a picker — emit `해당 task-group의 모든 task가 done 상태입니다. 생성할 schedule이 없습니다.` (or `해당 task-group을 찾을 수 없습니다.` when the catalog has no tasks at all) and stop **without creating a file**.
67
+ - The user **named no task-group, or the named token matches 0 or ≥2 groups** → present a 3-option picker via `AskUserQuestion` and do NOT proceed until the user chooses. Build the options from the catalog: walk `tasks[]` in catalog order (already `updatedAt` desc — see `scripts/okstra_ctl/render.py:654`), collect distinct `taskGroupPathSegment` values that have ≥1 entry whose resolved `workStatus` is **non-done** (defer to Step 2's inference table), and offer the newest **1–2** such groups as recommendations. The **last option is always `Enter directly`** (free-text group token, fed into sub-step 3). Label each recommendation with its non-done task count (e.g. `uploadFont (non-done 3)`).
68
+ - If **zero groups have a non-done task** (or the catalog is empty), do NOT open a picker — emit `All tasks in this task-group are done. There is no schedule to generate.` (or `That task-group could not be found.` when the catalog has no tasks at all) and stop **without creating a file**.
63
69
  3. **Normalise the resolved `<task-group>`:** lowercase it, then strip every character that is not `[a-z0-9]`. Apply the same transform to each entry's `taskGroupPathSegment`. Match on equality — this is the single comparison rule; do NOT also fall back to the raw `taskGroup` field.
64
- 4. If no tasks found, output `해당 task-group을 찾을 수 없습니다.` and stop.
70
+ 4. If no tasks found, output `That task-group could not be found.` and stop.
65
71
  5. For each matched task, read `.okstra/tasks/<task-group-segment>/<task-id-segment>/task-manifest.json` directly. Catalog data may be stale; the manifest is authoritative.
66
72
  6. **Derive `<project-id>`** for the header: prefer `task-catalog.json`'s top-level `projectId`, otherwise the first matched manifest's `projectId`. Do not invent a value.
67
73
 
@@ -69,7 +75,7 @@ One computation rule the template scaffold cannot carry inline:
69
75
 
70
76
  For inference when `workStatus` is missing or empty, defer to the inference table in `skills/okstra-inspect/SKILL.md` (`status.4` → "Default value convention") — do not duplicate it here. Then filter: resolved `done` → exclude; everything else (`todo` / `in-progress` / `blocked` / `phase-done` / inferred non-done) → include.
71
77
 
72
- If 0 tasks remain, output `해당 task-group의 모든 task가 done 상태입니다. 생성할 schedule이 없습니다.` and stop **without creating a file**.
78
+ If 0 tasks remain, output `All tasks in this task-group are done. There is no schedule to generate.` and stop **without creating a file**.
73
79
 
74
80
  ### Step 3: Per-task stage extraction (Stage Map source)
75
81
 
@@ -83,7 +89,7 @@ For each in-scope task, the **authoritative source is its `implementation-planni
83
89
 
84
90
  **No planning Stage Map** (`stage-map` returned `stages: []`) → this task cannot be stage-scheduled. Tag it `[NEEDS-PLANNING]`, skip the Step 3.5 stage picker for it, and render it under its phase section as a single banner line with task-level metadata only (no Gantt bars, no day total). Continue with the remaining tasks.
85
91
 
86
- **`remainingStages` is empty** (every stage done but `workStatus` not `done`) → not a scheduling target. Render the task as `_완료 — 남은 stage 없음_` under its phase section; contribute no forward day total.
92
+ **`remainingStages` is empty** (every stage done but `workStatus` not `done`) → not a scheduling target. Render the task as `_Complete — no remaining stage_` under its phase section; contribute no forward day total.
87
93
 
88
94
  ### Step 3.5: Stage selection (per task, user input)
89
95
 
@@ -96,8 +102,8 @@ Run this **once per in-scope task that has a non-empty `remainingStages`**, sequ
96
102
  3. If `remainingStages` has ≤2 stages, emit only the bundles that are distinct (1–2), never pad to 3.
97
103
 
98
104
  **Render the picker** with `AskUserQuestion` (one question for this task):
99
- - Options = the distinct bundles + a final `"남은 stage 전부"` option. `AskUserQuestion`'s built-in Other slot serves the `직접 입력` (arbitrary stage subset) case; when the user supplies a custom subset, close it under `depends_on` before accepting.
100
- - **Degenerate skip:** if only one distinct bundle exists AND it already equals all remaining stages, skip the picker for this task and set `selectedStages = remainingStages` (log `> _Stage picker 생략: 남은 stage가 단일 의존성 체인._`).
105
+ - Options = the distinct bundles + a final `"All remaining stages"` option. `AskUserQuestion`'s built-in Other slot serves the `Enter directly` (arbitrary stage subset) case; when the user supplies a custom subset, close it under `depends_on` before accepting.
106
+ - **Degenerate skip:** if only one distinct bundle exists AND it already equals all remaining stages, skip the picker for this task and set `selectedStages = remainingStages` (log `> _Stage picker skipped: remaining stages form a single dependency chain._`).
101
107
 
102
108
  Record the chosen `selectedStages` for this task. Any custom selection that breaks `depends_on` closure is rejected — re-prompt the same task.
103
109
 
@@ -110,19 +116,19 @@ Record the chosen `selectedStages` for this task. Any custom selection that brea
110
116
  | `bugfix` | Phase 1 when risk is High/Med-High; otherwise Phase 2 |
111
117
  | `feature` / `improvement` / `docs` / `doc` | Phase 2 |
112
118
  | `refactor` / `ops` | Phase 3 |
113
- | `unknown` (or unmatched / missing) | Phase 2, with rationale `> _workCategory '<raw-value>' 미정의 — Phase 2로 기본 분류._` at the top of that phase section |
119
+ | `unknown` (or unmatched / missing) | Phase 2, with rationale `> _workCategory '<raw-value>' undefined — defaulting to Phase 2._` at the top of that phase section |
114
120
 
115
121
  Priority overrides category: `P0` → Phase 1; `P1`/`P2` → Phase 2; `P3` or multi-repo + infrastructure scope → Phase 3. When still ambiguous, place the task in the closest phase and add a one-line rationale at the top of that phase section (not validator-enforced — emit it yourself).
116
122
 
117
- Phase bucketing stays **task-level** (a task lands in one Phase by its `workCategory`/Priority). Within a task's per-task section, the **selected stages become the Work Breakdown rows**: one row per `selectedStages` entry with its `title`, `step_count`-derived effort, and `depends_on`. Stages excluded from `selectedStages` because they are already done are listed once as `> _완료 stage: stage <n>, …_` and carry no forward effort. This keeps the mandatory heading skeleton unchanged while moving the unit of work to the stage.
123
+ Phase bucketing stays **task-level** (a task lands in one Phase by its `workCategory`/Priority). Within a task's per-task section, the **selected stages become the Work Breakdown rows**: one row per `selectedStages` entry with its `title`, `step_count`-derived effort, and `depends_on`. Stages excluded from `selectedStages` because they are already done are listed once as `> _Done stages: stage <n>, …_` and carry no forward effort. This keeps the mandatory heading skeleton unchanged while moving the unit of work to the stage.
118
124
 
119
125
  ### Step 5: Gantt decision (render by default)
120
126
 
121
- `## Gantt Chart` is **rendered by default** — skip ONLY when literally no day signal exists (every task is effort=XXL with no visible decomposition, or all tasks lack both effort sizing and decomposition). Render whenever any of these hold: 2+ tasks with effort sizing; 1 task whose effort yields a range (mid-point bar, or `lo`/`hi` two-bar form); 1 task with Part/Phase/Step decomposition in the source (bars at decomposition-unit level); total estimated effort ≥ 3 days. When per-unit day allocations aren't itemized, split the parent range across the visible units yourself and append the `est` annotation (or add `> 일별 배분은 추정치이며 차단 항목 해소 후 갱신 권장.`). "Range is wide", "single task", "user decisions pending" are NOT skip reasons — render an estimate-tagged chart instead.
127
+ `## Gantt Chart` is **rendered by default** — skip ONLY when literally no day signal exists (every task is effort=XXL with no visible decomposition, or all tasks lack both effort sizing and decomposition). Render whenever any of these hold: 2+ tasks with effort sizing; 1 task whose effort yields a range (mid-point bar, or `lo`/`hi` two-bar form); 1 task with Part/Phase/Step decomposition in the source (bars at decomposition-unit level); total estimated effort ≥ 3 days. When per-unit day allocations aren't itemized, split the parent range across the visible units yourself and append the `est` annotation (or add `> Per-day allocation is an estimate; refresh recommended after blocking items are resolved.`). "Range is wide", "single task", "user decisions pending" are NOT skip reasons — render an estimate-tagged chart instead.
122
128
 
123
129
  When the source is a Stage Map, the Gantt **bars are the selected stages** (one bar per `selectedStages` entry), day length split from `step_count` (or the effort range across the stage's steps), and cross-stage `(after stage <n>)` / `(after <TASK-ID>)` edges follow `depends_on`. Already-done stages never get a bar. A task tagged `[NEEDS-PLANNING]` contributes no bars.
124
130
 
125
- When you do skip, insert in the section's position exactly: `> _Gantt Chart 생략: <concrete reason referencing the actual data>._`
131
+ When you do skip, insert in the section's position exactly: `> _Gantt Chart skipped: <concrete reason referencing the actual data>._`
126
132
 
127
133
  **Directive override (highest priority).** Before applying the heuristic, check for a `## Directive` section, first hit wins: (1) the `--directive-file <abs-path>` argument; (2) `<PROJECT_ROOT>/.okstra/tasks/<task-group-segment>/schedule/instruction-set/analysis-material.md`; (3) none → apply the default heuristic silently. A found directive overrides the render/skip heuristic for the affected section — note it inline as `> _Per Directive directive: <verbatim short excerpt>._` — and its pre-supplied day allocations / phase weights are used verbatim as bar lengths. A directive file without a `## Directive` heading counts as "no directive".
128
134
 
@@ -159,11 +165,11 @@ Reached only after Step 5.5 returns `pass`. **Promote** the verified staging dra
159
165
  ### Step 8: Completion message (Korean)
160
166
 
161
167
  ```
162
- ✓ Schedule 생성 완료: <relative-path>
163
- - 포함 task: N개
164
- - 제외(done) task: M개
165
- - 예상 소요: X.X ~ Y.Y days (Effort 합산)
166
- - 모드: lead + verifier
168
+ ✓ Schedule generated: <relative-path>
169
+ - Included tasks: N
170
+ - Excluded (done) tasks: M
171
+ - Estimated effort: X.X ~ Y.Y days (Effort sum)
172
+ - Mode: lead + verifier
167
173
  ```
168
174
 
169
175
  ## Edge Cases
@@ -171,12 +177,12 @@ Reached only after Step 5.5 returns `pass`. **Promote** the verified staging dra
171
177
  | Case | Handling |
172
178
  |------|----------|
173
179
  | `workStatus` absent or empty | Resolve via the `okstra-inspect status.4` inference table; include unless resolved `done` |
174
- | Filtered task count is 0 | Emit "모든 task가 done" message; do NOT create a file |
175
- | implementation-planning 리포트 없음 | `[NEEDS-PLANNING]` 배너로만 나열, stage picker·Gantt·day total 없음 |
176
- | 남은 stage 0개(전부 done, workStatus 미표기) | `_완료 — 남은 stage 없음_`, forward 산출 없음 |
177
- | 남은 stage ≤2개 | 누적 묶음을 나오는 만큼만(1~2), 단일 체인이면 picker 생략·전부 진행 |
178
- | verifier 2회 미통과 | 최종 파일 미작성, 잔여 missing/extra/order 사용자 보고 |
179
- | `task-group` matches no tasks | "해당 task-group을 찾을 수 없습니다." and stop |
180
+ | Filtered task count is 0 | Emit the "all tasks are done" message; do NOT create a file |
181
+ | No implementation-planning report | List with a `[NEEDS-PLANNING]` banner only; no stage picker / Gantt / day total |
182
+ | 0 remaining stages (all done, workStatus unmarked) | `_Complete — no remaining stage_`, no forward computation |
183
+ | ≤2 remaining stages | Emit only as many cumulative bundles as arise (1–2); a single chain skips the picker and proceeds with all |
184
+ | verifier fails twice | Final file not written; report residual missing/extra/order to the user |
185
+ | `task-group` matches no tasks | "That task-group could not be found." and stop |
180
186
  | Catalog and manifest disagree on `workStatus` | Manifest wins (catalog may be stale) |
181
187
  | task-group casing / punctuation variants | Normalise both sides (lowercase + strip non-`[a-z0-9]`), compare against `taskGroupPathSegment` only; use the manifest's segment verbatim for path output |
182
188
 
@@ -140,7 +140,7 @@ Inform the user with a short summary:
140
140
  | `command not found: npx` | Node missing | Install node 18+. |
141
141
  | `okstra ensure-installed` keeps reinstalling | `~/.okstra/version` write fails (permissions) | Check `~/.okstra` ownership and writability. |
142
142
  | `error: --project-id is required (no existing project.json, not a TTY)` | `okstra setup --yes` invoked without `--project-id`, or with empty answer to Step 3 prompt | Re-ask Step 3 and pass a non-empty id via `--project-id`. |
143
- | `projectId mismatch` / `projectId 불일치` | `project.json` already exists with a different id | Decide which id is canonical; manually delete `<PROJECT_ROOT>/.okstra/project.json` to re-register, or re-run with the existing id. |
143
+ | `projectId mismatch` / `projectId mismatch` | `project.json` already exists with a different id | Decide which id is canonical; manually delete `<PROJECT_ROOT>/.okstra/project.json` to re-register, or re-run with the existing id. |
144
144
  | `EACCES` writing under `.okstra/` | directory owned by another user (e.g. created by a previous root-shell run) | `chown -R "$USER" <PROJECT_ROOT>/.okstra` or delete and let setup recreate. |
145
145
  | `warning: failed to provision .claude/settings.local.json symlink` | a non-symlink `.claude/settings.local.json` already exists and the backup-and-replace step failed | Inspect `<PROJECT_ROOT>/.claude/settings.local.json{,.bak.*}`; manually merge project-specific rules, then re-run setup. |
146
146
  | `npx okstra@latest install` succeeds but `doctor` shows FAIL | runtime/{python,bin,skills} sync not yet performed (pre-release package) | Use dev install: clone the repo and run `node bin/okstra install --link <repo>`. |
@@ -140,19 +140,19 @@ overwriting silently.
140
140
  `~/.okstra/templates/prd/pr-body.template.md`. Most projects want their own
141
141
  (e.g. `.github/PULL_REQUEST_TEMPLATE.md`). Pre-registration during setup is
142
142
  opt-in; the same prompt is offered again on the first `release-handoff` run,
143
- so deferring (`나중에`) is safe.
143
+ so deferring (`Later`) is safe.
144
144
 
145
145
  Ask with `AskUserQuestion` (fixed options — file path entry happens in the
146
146
  follow-up plain text prompt):
147
147
 
148
- - **Question**: `"이 프로젝트에서 release-handoff 가 사용할 PR 본문 템플릿을 등록할까요?"`
148
+ - **Question**: `"Register the PR body template that release-handoff will use for this project?"`
149
149
  - **Options**:
150
- 1. `이번 프로젝트만 (project scope)` — writes `prTemplatePath` to `<PROJECT_ROOT>/.okstra/project.json`.
151
- 2. `전역 (global scope)` — writes `prTemplatePath` to `~/.okstra/config.json`.
152
- 3. `나중에` — skip.
150
+ 1. `This project only (project scope)` — writes `prTemplatePath` to `<PROJECT_ROOT>/.okstra/project.json`.
151
+ 2. `Global (global scope)` — writes `prTemplatePath` to `~/.okstra/config.json`.
152
+ 3. `Later` — skip.
153
153
 
154
154
  If scope 1 or 2, follow up with a plain text prompt:
155
- `"PR 본문 템플릿 파일 경로를 알려주세요. project 스코프는 project-root 기준 상대경로 또는 절대경로, global 스코프는 절대경로 또는 ~/ 시작 경로만 허용됩니다."`
155
+ `"Tell me the PR body template file path. project scope accepts a project-root-relative or absolute path; global scope accepts only an absolute path or a ~/ path."`
156
156
  Consume the next user message, then run:
157
157
 
158
158
  ```bash
@@ -160,23 +160,24 @@ okstra config set pr-template-path "<typed-path>" --scope <project|global>
160
160
  ```
161
161
 
162
162
  The command validates the value (global rejects relative paths) and writes
163
- atomically. Surface its stdout JSON. If the user chose `나중에`, tell them
163
+ atomically. Surface its stdout JSON. If the user chose `Later`, tell them
164
164
  they can register later via the same `okstra config set` command or the
165
165
  per-run override prompt during the next release-handoff run.
166
166
 
167
167
  ## E. Final report language (`reportLanguage`)
168
168
 
169
- 기본은 영어. Skip 하면 `reportLanguage` 필드를 두지 않고 runtime 이 `auto` 로
170
- 처리한다 — task brief 의 주 서술 언어를 따라간다 (한국어 brief → 한국어
171
- report, 영어 brief → 영어 report).
169
+ The default is English. If you skip, no `reportLanguage` field is written and
170
+ the runtime treats it as `auto` — it follows the task brief's primary
171
+ narration language (Korean brief → Korean report, English brief → English
172
+ report).
172
173
 
173
174
  AskUserQuestion (fixed options):
174
- - Question: `"Final report 를 어느 언어로 작성할까요?"`
175
+ - Question: `"Which language should the final report be written in?"`
175
176
  - Options:
176
177
  1. `English (recommended)` → `en`
177
- 2. `한국어` → `ko`
178
- 3. `Auto (task brief 언어로 추론, 불분명하면 영어)` → `auto`
179
- 4. `나중에` → skip (필드 미설정 → runtime 이 auto 로 처리)
178
+ 2. `Korean` → `ko`
179
+ 3. `Auto (infer from the task brief language; English if unclear)` → `auto`
180
+ 4. `Later` → skip (no field set → runtime treats it as auto)
180
181
 
181
182
  If the user picks 1/2/3:
182
183
 
@@ -184,5 +185,5 @@ If the user picks 1/2/3:
184
185
  okstra config set report-language <en|ko|auto> --scope project
185
186
  ```
186
187
 
187
- 전역 기본값은 README 의 "global config" 안내대로 `--scope global` 로 수동
188
- 설정한다 — 이 flow 에서는 project 스코프만 제공한다.
188
+ Set the global default manually with `--scope global` as described in the
189
+ README's "global config" guidance — this flow offers only project scope.
@@ -21,8 +21,11 @@ okstra preflight --runtime claude-code --json
21
21
  ```
22
22
 
23
23
  On `ok:true`, carry `projectRoot`. On `ok:false`, tell the user to run `/okstra-setup` first
24
- and stop. On `unknown command: preflight`, tell the user to run
25
- `npm i -g okstra@latest` and stop.
24
+ and stop.
25
+
26
+ <!-- BEGIN FRAGMENT: preflight-outdated-cli -->
27
+ If the call fails with `unknown command: preflight`, the `okstra` binary on PATH predates this skill — tell the user to update it (`npm i -g okstra@latest`), then stop (`/okstra-setup` does not update the binary).
28
+ <!-- END FRAGMENT: preflight-outdated-cli -->
26
29
 
27
30
  ## Step 1: Resolve the day window
28
31