okstra 0.214.1 → 0.216.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 (118) hide show
  1. package/README.md +2 -1
  2. package/dist/cli-registry.d.mts +14 -0
  3. package/dist/cli-registry.mjs +12 -0
  4. package/dist/cli-registry.mjs.map +1 -1
  5. package/dist/commands/chat/chat.mjs +2 -56
  6. package/dist/commands/chat/chat.mjs.map +1 -1
  7. package/dist/commands/execute/render-bundle.mjs +5 -2
  8. package/dist/commands/execute/render-bundle.mjs.map +1 -1
  9. package/dist/commands/execute/wizard.mjs +6 -0
  10. package/dist/commands/execute/wizard.mjs.map +1 -1
  11. package/dist/commands/memory/memory.mjs +57 -6
  12. package/dist/commands/memory/memory.mjs.map +1 -1
  13. package/dist/lib/file-lock.d.mts +1 -0
  14. package/dist/lib/file-lock.mjs +60 -0
  15. package/dist/lib/file-lock.mjs.map +1 -0
  16. package/dist/lib/python-command.mjs +11 -1
  17. package/dist/lib/python-command.mjs.map +1 -1
  18. package/dist/lib/skill-catalog.mjs +1 -0
  19. package/dist/lib/skill-catalog.mjs.map +1 -1
  20. package/dist/lib/types.d.mts +1 -1
  21. package/docs/architecture.md +2 -2
  22. package/docs/cli.md +36 -14
  23. package/docs/project-structure-overview.md +6 -3
  24. package/package.json +1 -1
  25. package/runtime/BUILD.json +2 -2
  26. package/runtime/bin/lib/okstra/cli.sh +9 -2
  27. package/runtime/bin/lib/okstra/globals.sh +2 -0
  28. package/runtime/bin/lib/okstra/interactive.sh +8 -2
  29. package/runtime/bin/lib/okstra/usage.sh +3 -1
  30. package/runtime/bin/okstra.sh +7 -1
  31. package/runtime/prompts/duties/discussion-worker.json +32 -0
  32. package/runtime/prompts/lead/convergence.md +58 -136
  33. package/runtime/prompts/lead/okstra-lead-contract.md +45 -12
  34. package/runtime/prompts/profiles/_common-contract.md +1 -1
  35. package/runtime/prompts/wizard/prompts.ko.json +14 -1
  36. package/runtime/python/okstra_ctl/adapters/providers/antigravity/adapter.py +4 -0
  37. package/runtime/python/okstra_ctl/adapters/providers/claude/adapter.py +4 -11
  38. package/runtime/python/okstra_ctl/agent/invocation.py +1 -0
  39. package/runtime/python/okstra_ctl/agent/prompt_cli/dynamic_verifier.py +2 -2
  40. package/runtime/python/okstra_ctl/agent/prompt_cli/materialize.py +4 -4
  41. package/runtime/python/okstra_ctl/agent/prompt_cli/run_identity.py +2 -2
  42. package/runtime/python/okstra_ctl/analysis_packet.py +3 -0
  43. package/runtime/python/okstra_ctl/consumers.py +8 -2
  44. package/runtime/python/okstra_ctl/convergence.py +143 -29
  45. package/runtime/python/okstra_ctl/convergence_critic_prompt.py +57 -0
  46. package/runtime/python/okstra_ctl/convergence_critic_verify_prompt.py +106 -0
  47. package/runtime/python/okstra_ctl/convergence_engine.py +140 -95
  48. package/runtime/python/okstra_ctl/convergence_reverify_prompt.py +3 -27
  49. package/runtime/python/okstra_ctl/convergence_store.py +11 -4
  50. package/runtime/python/okstra_ctl/dispatch_core.py +4 -1
  51. package/runtime/python/okstra_ctl/domain/role.py +1 -0
  52. package/runtime/python/okstra_ctl/manager_cli.py +27 -1
  53. package/runtime/python/okstra_ctl/manager_launch.py +44 -4
  54. package/runtime/python/okstra_ctl/manager_sync.py +14 -1
  55. package/runtime/python/okstra_ctl/manager_view.py +53 -1
  56. package/runtime/python/okstra_ctl/next_phase.py +1 -0
  57. package/runtime/python/okstra_ctl/phases/catalog.py +2 -1
  58. package/runtime/python/okstra_ctl/phases/discussion/__init__.py +1 -0
  59. package/runtime/python/okstra_ctl/phases/discussion/boundary.json +11 -0
  60. package/runtime/python/okstra_ctl/phases/discussion/cli.py +512 -0
  61. package/runtime/python/okstra_ctl/phases/discussion/entry.py +116 -0
  62. package/runtime/python/okstra_ctl/phases/discussion/followup.py +876 -0
  63. package/runtime/python/okstra_ctl/phases/discussion/memory.py +294 -0
  64. package/runtime/python/okstra_ctl/phases/discussion/overview.py +85 -0
  65. package/runtime/python/okstra_ctl/phases/discussion/profile.json +31 -0
  66. package/runtime/python/okstra_ctl/phases/discussion/profile.md +48 -0
  67. package/runtime/python/okstra_ctl/phases/discussion/report.py +73 -0
  68. package/runtime/python/okstra_ctl/phases/discussion/report_assets/discussion.template.html +15 -0
  69. package/runtime/python/okstra_ctl/phases/discussion/report_assets/discussion.template.md +6 -0
  70. package/runtime/python/okstra_ctl/phases/discussion/spec.md +197 -0
  71. package/runtime/python/okstra_ctl/phases/discussion/store.py +1143 -0
  72. package/runtime/python/okstra_ctl/phases/discussion/validation.py +111 -0
  73. package/runtime/python/okstra_ctl/phases/discussion/wizard.py +90 -0
  74. package/runtime/python/okstra_ctl/phases/discussion/workflow.py +667 -0
  75. package/runtime/python/okstra_ctl/phases/implementation/instructions/_implementation-deliverable.md +1 -1
  76. package/runtime/python/okstra_ctl/phases/implementation_planning/host-rules.md +4 -1
  77. package/runtime/python/okstra_ctl/phases/implementation_planning/instructions/plan-body-verification.md +1 -1
  78. package/runtime/python/okstra_ctl/phases/requirements_discovery/spec.md +1 -1
  79. package/runtime/python/okstra_ctl/plan_run_root.py +8 -0
  80. package/runtime/python/okstra_ctl/render.py +7 -20
  81. package/runtime/python/okstra_ctl/render_final_report.py +2 -1
  82. package/runtime/python/okstra_ctl/report_assembly.py +22 -0
  83. package/runtime/python/okstra_ctl/report_contract.py +2 -0
  84. package/runtime/python/okstra_ctl/report_narrative.py +13 -4
  85. package/runtime/python/okstra_ctl/report_synthesis_packet.py +10 -0
  86. package/runtime/python/okstra_ctl/run.py +40 -7
  87. package/runtime/python/okstra_ctl/stage_close.py +46 -11
  88. package/runtime/python/okstra_ctl/stage_reopen.py +151 -0
  89. package/runtime/python/okstra_ctl/verdict_blocks.py +11 -35
  90. package/runtime/python/okstra_ctl/wizard/cli.py +8 -0
  91. package/runtime/python/okstra_ctl/wizard/confirmation.py +4 -1
  92. package/runtime/python/okstra_ctl/wizard/engine.py +8 -0
  93. package/runtime/python/okstra_ctl/wizard/ids.py +12 -0
  94. package/runtime/python/okstra_ctl/wizard/registry.py +35 -3
  95. package/runtime/python/okstra_ctl/wizard/render.py +4 -1
  96. package/runtime/python/okstra_ctl/wizard/sources.py +2 -2
  97. package/runtime/python/okstra_ctl/wizard/state.py +11 -1
  98. package/runtime/python/okstra_ctl/wizard/steps_identity.py +4 -4
  99. package/runtime/python/okstra_ctl/worker_prompt_policy.py +24 -6
  100. package/runtime/python/okstra_token_usage/pricing.py +2 -0
  101. package/runtime/schemas/convergence-groups-v1.0.schema.json +1 -1
  102. package/runtime/schemas/convergence-groups-v2.0.schema.json +1 -1
  103. package/runtime/schemas/final-report-v3.0.schema.json +90 -1
  104. package/runtime/skills/_fragments/wizard-bash-invocation-rule.md +1 -0
  105. package/runtime/skills/_fragments/wizard-confirm-step.md +7 -0
  106. package/runtime/skills/_fragments/wizard-lead-takeover.md +8 -0
  107. package/runtime/skills/_fragments/wizard-preflight.md +75 -0
  108. package/runtime/skills/_fragments/wizard-prompt-loop.md +22 -0
  109. package/runtime/skills/_fragments/wizard-relay-protocol.md +40 -0
  110. package/runtime/skills/_fragments/wizard-render-bundle.md +74 -0
  111. package/runtime/skills/okstra-discussion/SKILL.md +347 -0
  112. package/runtime/skills/okstra-pr-gen/SKILL.md +7 -0
  113. package/runtime/skills/okstra-run/SKILL.md +22 -2
  114. package/runtime/templates/manager/view.template.html +3 -0
  115. package/runtime/templates/reports/html/i18n/en.json +5 -0
  116. package/runtime/templates/reports/html/i18n/ko.json +5 -0
  117. package/runtime/validators/validate-brief.py +3 -2
  118. package/runtime/validators/validate-run.py +4 -6
@@ -0,0 +1,347 @@
1
+ ---
2
+ name: okstra-discussion
3
+ description: Use when the user wants several agents to research a ticket or project with them and settle its requirements and implementation direction before any planning, starting from a link or a short description instead of a finished brief. Also use to continue, complete or re-discuss such a discussion and to write its follow-up briefs. Trigger words include "okstra discussion", "discuss this with the agents", "settle the requirements together", "debate the direction", "continue the discussion", "re-discuss". NOT for running a lifecycle phase (okstra-run), writing a brief from requirements (okstra-brief-gen), or answering a finished report's clarifications (okstra-user-response).
4
+ ---
5
+
6
+ # OKSTRA Discussion (in-session)
7
+
8
+ Start or continue a `discussion` task — the user and several agents research a ticket or project, cross-check each other's claims, and the user settles the requirements and direction. The wizard collects the inputs with `okstra wizard init --task-type discussion`, `okstra render-bundle` prepares the run through `prepare_task_bundle()`, and the current session then takes over as `Okstra lead`, acting as the facilitator. `/okstra-run` reaches the same call when the user picks `discussion`; this skill adds what happens between runs: one research cycle per run, completion, follow-ups and Memory Book recovery.
9
+
10
+ A discussion needs no manager. `okstra manager task sync` reads the discussion of a manager task's child by reference; it never starts or completes one.
11
+
12
+ <!-- BEGIN FRAGMENT: wizard-bash-invocation-rule -->
13
+ **Bash invocation rule (permission-friendly)**: every Bash command in this skill begins with the literal token `okstra` (or another already-allowed binary) and passes 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.
14
+ <!-- END FRAGMENT: wizard-bash-invocation-rule -->
15
+
16
+ ## When to Use
17
+
18
+ - The user wants agents to research and argue a subject with them before a follow-up task plans it ("discuss PROD-1623 with the agents", "let's settle the search ranking direction first").
19
+ - The user wants to continue a discussion, run another research cycle, complete it, re-discuss a completed one, or write a follow-up brief from it.
20
+
21
+ ## When NOT to Use
22
+
23
+ - The user already has a brief and wants a lifecycle phase — use `okstra-run`.
24
+ - The user wants a brief written from requirements or a ticket — use `okstra-brief-gen`.
25
+ - The user wants to answer a finished report's open questions — use `okstra-user-response`.
26
+
27
+ <!-- BEGIN FRAGMENT: wizard-relay-protocol -->
28
+ ## How the wizard talks to you
29
+
30
+ Every wizard call returns JSON. The two shapes you'll see:
31
+
32
+ ```json
33
+ { "ok": true, "echo": "task-group: backend-api",
34
+ "next": { "step": "task_id", "kind": "text", "label": "...", "options": [], "echoTemplate": "...",
35
+ "progress": { "index": 5, "total": 11, "remaining": 6 } } }
36
+ ```
37
+
38
+ 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. role-add or extra role-model slots), `remaining` (`total − index`), and **`label`** — the ready-to-render marker the wizard already composed (e.g. `Step 10/11 · 앞으로 1 스텝 남음`, or `Step 11/11 · 마지막 단계` on the final screen; the suffix is Korean whatever the report language). **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.
39
+
40
+ ```json
41
+ { "ok": false, "error": "approved plan §1 has 1 unresolved `Blocks=approval` row(s); ...",
42
+ "current": { "step": "approved_plan", "kind": "text", "label": "..." } }
43
+ ```
44
+
45
+ On `ok: false`, re-prompt with the same `current.step` using the error message. The wizard never advances on validation failure; the user retries the same step. **`current` may be `null`** when the current step itself cannot render (e.g. the approved plan's Stage Map is corrupt) — that case is terminal: show `error` and stop, the user must fix the plan file before retrying. Never re-prompt off a `null` `current`.
46
+
47
+ The wizard tells you which relay operation to use via `next.interaction.kind`. Step 1 loads the current registered host's relay contract. Use the matching entry under its `interactions` object, including the runtime-generated navigation and completion options. If the entry is absent, stop instead of inventing a host function or falling back silently. Use the explicit `Legacy text relay compatibility` mapping only when the preflight response has no `relayContract`.
48
+
49
+ - `native-single` → use the current registered host's relay contract for its native single-select function. Pass every option in its original order and submit the selected `options[].value`.
50
+ - `native-multi` → use the relay contract's native multi-select function. Pass every option in its original order and submit the selected values as one CSV answer; submit an empty selection as `--answer ""`.
51
+ - `native-group` → use the relay contract's native grouped-question function once. Preserve every question and option, then submit one JSON object keyed by `questions[].step`; when a grouped question is multi-select, its step value is the selected option values joined as one CSV string, not a JSON array.
52
+ - `numbered-single` → render every option as a 1-based numbered Markdown list and submit the user's next message unchanged.
53
+ - `numbered-multi` → render every option as a 1-based numbered Markdown list and submit the user's comma-separated reply unchanged.
54
+ - `sequential-group` → render every question in order, preserve each complete option list, collect every answer, and submit one JSON object keyed by `questions[].step`.
55
+ - `plain-text` → write `label` as plain text and submit the user's next message unchanged.
56
+ - `kind: "done"` → input collection finished; move to Step 5.
57
+ - `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).
58
+
59
+ When native selection is available, the runtime keeps choices in that control or in same-screen question tabs when the complete selection fits. Otherwise, it returns a complete numbered list: show all labels and descriptions in one message and accept a number, label, or value (comma-separated for multiple choices). This applies to plan confirmation, stages, role counts, and provider/model lists. Do not paginate choices or add a next-page option. Follow the returned `interaction.kind`, including numbered interactions when the user generally prefers a selector. If the declared selector itself fails, follow the relay's `recovery` object. Genuine text steps still collect text.
60
+
61
+ Submit the answer shape required by `interaction.answerProtocol`; do not add normalization beyond the registered relay's explicit mapping. Invalid, out-of-range, or ambiguous answers return `ok: false` and must re-render the same complete interaction.
62
+
63
+ 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.
64
+
65
+ Never invent additional questions. **Never drop, hide, merge, reorder, or truncate** a `pick` / `pick_group` option — relay every `options[]` entry, including the flagged recommendation. Do not collapse a multi-option pick into a "recommended + Enter directly / Other" shortlist. The wizard's arrays are the complete authoritative choice sets, regardless of the current host UI's usual option limit. The run-prompt recommendation rule (1–2 recommendations + Enter directly) shapes the **option set** only for prompts this skill authors itself, never for wizard-provided options — you may not add, drop, or reorder a wizard option to produce a shortlist. It does not excuse you from recommending: before relaying a wizard step whose answer turns on something readable (the carried report, the sidecar the user already wrote, the prior Stage Map), read it, put what you found in the question body, and name which of the wizard's own options you recommend and why. Relaying a step with no context and no recommendation hands the whole question back to the user — see the lifecycle core contract "Asking the user (BLOCKING)".
66
+
67
+ **One recommendation, shown in one place, and it is option 1.** Wizard labels carry no recommendation marker of their own. The option carrying `recommended: true` is what this run computed, and the wizard already placed it first — except on model-selection steps (`role-models:*`, `role-model:*`), which keep provider display order, so the flagged model may sit anywhere in the list. Append ` (추천)` to that option's label when you render it, and to no other. A checkbox step (`multi: true`) may flag several leading options: they are the recommended set, rendered the same way. Your prose recommendation names one of the flagged options. If you believe a different option is right, do not quietly recommend it in prose while the flagged one still reads as recommended on screen: say you disagree, name both, and let the user pick. When no option carries the flag, the run computed nothing for this step — recommend one from what you read and mark that one, and do not present the first option as a default just because it is first. **Enforced:** `scripts/okstra_ctl/wizard/state.py` `Prompt.__post_init__` refuses a step whose free-input option is not last, whose single-select recommendation is not exactly one option, whose recommendation is not placed first on a single-select step or is not the leading run on a checkbox step (both position checks are skipped on `role-models:*` / `role-model:*` steps), or that marks the free-input / abort escape as a recommendation.
68
+ <!-- END FRAGMENT: wizard-relay-protocol -->
69
+
70
+ <!-- BEGIN FRAGMENT: wizard-preflight -->
71
+ ## Step 1: Preflight
72
+
73
+ Use the launcher's `OKSTRA_RUNTIME_HOST` value when it is present. Otherwise use the registered host ID that the current harness declares for this session. This is a harness capability declaration, never a worker-provider choice. Do not infer it from an executable or `PATH`, and do not silently substitute another host.
74
+
75
+ Record the semantic functions that the live harness can actually perform. Always include `plain_text_input`. Record `native_single_select`, `native_multi_select`, or `native_question_group` only when the current harness exposes that function. Do not infer functions from the host ID; the effective declaration is computed from the relay contract after preflight.
76
+
77
+ The host-native provider runs as the current lead session when its role is supported. Other selected providers run through their registered CLI wrappers. Claude remains only the model-policy default; it is not the process owner.
78
+
79
+ Run one Bash tool call (Bash invocation rule from the top of this file applies):
80
+
81
+ ```bash
82
+ okstra preflight --runtime <host-runtime>
83
+ ```
84
+
85
+ Read the fixed text lines. If the first line is `Okstra preflight: failed`, show `Runtime readiness` and every repeated `Readiness check`, `Readiness check status`, and `Readiness check action`, then stop before Step 2. Show `Reason` and `Recovery` for every failure. Do not relabel a host login, license, daemon, trust, or other readiness failure as missing Okstra setup. 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). Do **not** try to invoke `npx -y okstra@latest ...` as a fallback — `npx` is not on the literal-token allow-list and will force a confirmation prompt on every wizard call afterward. Every subsequent `okstra <subcmd>` call self-bootstraps its Python path, so never `export PYTHONPATH=...`.
86
+
87
+ On `Okstra preflight: ready`, require `Runtime readiness: ready` before Step 2. These checks come from the selected host adapter and are independent of worker-provider choices. Any other readiness value is unrecognized runtime output: show it and stop before Step 2 instead of guessing that the host is ready.
88
+
89
+ Carry the fixed `Lead entry mode` line into Step 2's `--entry-mode` as a literal. It is the mode the run must launch in, which is not always the mode this session is in: a host adapter may report that this session cannot host the lead while the run itself is ready. `spawn-process` means okstra starts its own lead process and this session is not the lead — when the line reads `spawn-process` and a readiness check carries `action: spawn-unsandboxed-codex-lead`, tell the user in one line that their current Codex session is sandboxed so okstra will open an unsandboxed lead instead, then continue. Never substitute `current-session` for a `spawn-process` answer, and never recompute the value from the host ID.
90
+
91
+ For a ready response, read the absolute path in the fixed `Relay contract` line with the current host's file-read primitive. Do not derive the path from the host ID or search `PATH`. In that file, find the `Wizard interaction relay` JSON block, require `schemaVersion: 1` and `runtime` equal to the fixed `Runtime` line, then take its `semanticFunctions` allowlist and intersect it with the functions the live harness exposes. The live harness does not expose tools named `native_single_select`. Map each allowlist token to the matching `interactions` kind (`native_single_select` → `native-single`, `native_multi_select` → `native-multi`, `native_question_group` → `native-group`). Include the token in the intersection only when this session can call the string in that kind's `function` field. If the kind is absent from `interactions`, omit the token. Pass only that intersection to Step 2; `plain_text_input` must be present. Keep the parsed `interactions` object for Step 3's function/input/response conversion, and the parsed `recovery` object for the branch below. An unreadable file, malformed block, runtime mismatch, absent `plain_text_input`, or later interaction kind missing from the object is a host relay contract failure: show the problem and stop rather than guessing.
92
+
93
+ If the successful fixed projection has `Relay contract: -`, enter the compatibility branch below. In that branch only, declare `plain_text_input` and keep its built-in `interactions` mapping for Step 3; do not assume a native tool from the host ID. The existing `unknown command: preflight` branch remains the authoritative stale-CLI failure.
94
+
95
+ Before calculating the effective intersection, apply the registered relay's client-specific tool selection and live mode restrictions. A callable question tool does not necessarily render a selector in the current client. In Codex, use the synchronous picker when permitted; asynchronous question cards are a desktop fallback, not a terminal picker. A declared native function can still fail at call time: the client refuses the call, the session has no view to present it in, or it returns no answer. That is not a reason to stop, and not a reason to try a different native function. Follow the relay's `recovery.native-question-refused` entry the first time it happens — drop the tokens it names from the intersection, keep `plain_text_input`, tell the user in one line that the host picker is unavailable, and render every remaining screen through the text mapping. Numbered text is that entry's own recovery path, so the ban on printing a numbered list does not apply once it fires. The wizard state file is untouched by a refused call: `okstra wizard step --state-file <path> --no-submit` returns the pending prompt again.
96
+
97
+ Plan adoption (`approve_plan_confirm`) is a workflow choice: present the wizard's existing options using the same selector as other `pick` steps. Follow the relay's distinction between plan decisions and execution permissions; the word "approval" alone is not a reason to replace a selector with a typed confirmation.
98
+
99
+ ### Legacy text relay compatibility
100
+
101
+ Use this built-in mapping only when the successful preflight response omitted `relayContract`. It is not a fallback for an unreadable, malformed, mismatched, or incomplete relay contract. If a present relay contract omits the wizard's interaction kind, keep the fail-closed rule above and stop.
102
+
103
+ ```json
104
+ {
105
+ "scope": "preflight-response-without-relayContract-only",
106
+ "semanticFunctions": ["plain_text_input"],
107
+ "interactions": {
108
+ "numbered-single": {
109
+ "function": "host-text",
110
+ "input": {
111
+ "question": "label-with-progress",
112
+ "options": "all-in-original-order-as-numbered-markdown-label-and-description"
113
+ },
114
+ "response": { "source": "next-message", "submit": "raw" }
115
+ },
116
+ "numbered-multi": {
117
+ "function": "host-text",
118
+ "input": {
119
+ "question": "label-with-progress",
120
+ "options": "all-in-original-order-as-numbered-markdown-label-and-description"
121
+ },
122
+ "response": { "source": "next-message", "submit": "raw" }
123
+ },
124
+ "sequential-group": {
125
+ "function": "host-text",
126
+ "input": {
127
+ "questions": "all-one-at-a-time-in-original-order",
128
+ "question": "label-with-progress",
129
+ "options": "all-in-original-order-as-numbered-markdown-label-and-description"
130
+ },
131
+ "response": {
132
+ "source": "next-message-by-question-position",
133
+ "submit": "compact-step-json-raw"
134
+ }
135
+ },
136
+ "plain-text": {
137
+ "function": "host-text",
138
+ "input": { "question": "label-with-progress" },
139
+ "response": { "source": "next-message", "submit": "raw" }
140
+ }
141
+ }
142
+ }
143
+ ```
144
+
145
+ For numbered interactions, render every option as a 1-based Markdown item containing its label followed by its description verbatim, in original order. For `sequential-group`, ask every question in order and build one compact JSON object keyed by `questions[].step`. Submit each user message unchanged; the wizard owns numbered and grouped normalization.
146
+ <!-- END FRAGMENT: wizard-preflight -->
147
+
148
+ The supported lead hosts are a `claude-code` lead, or any lead under cmux. `render-bundle` refuses any other lead before it writes a task artifact; show that refusal and stop instead of moving the user to another session.
149
+
150
+ ## Step 2: Initialize the wizard for a discussion
151
+
152
+ Generate a state-file path:
153
+
154
+ ```bash
155
+ okstra wizard new-state-file
156
+ ```
157
+
158
+ Then initialize with the literal values from Step 1 and `--task-type discussion`. Pass `--preferred-task-key <project-id:task-group:task-id>` when the user named a discussion task or this conversation already selected one (the same carry-forward rule as `/okstra-run`); omit it otherwise.
159
+
160
+ ```bash
161
+ okstra wizard init \
162
+ --state-file /var/folders/.../okstra-wizard.AbCd.json \
163
+ --project-root /abs/path/to/project \
164
+ --project-id my-project-id \
165
+ --host-runtime <host-runtime> \
166
+ --entry-mode <lead-entry-mode> \
167
+ --task-type discussion \
168
+ --available-function plain_text_input \
169
+ --available-function <each-additional-function-in-the-effective-intersection>
170
+ ```
171
+
172
+ `--task-type discussion` makes a new task ask the task type before the brief, with `discussion` recommended, and replaces the brief question with the start input. An existing task keeps `discussion` as its type and continues its discussion.
173
+
174
+ ## Step 3: Run the prompt loop
175
+
176
+ <!-- BEGIN FRAGMENT: wizard-prompt-loop -->
177
+ Repeat until `next.kind == "done"` (or `"aborted"` — terminal cancel, see "How the wizard talks to you"):
178
+
179
+ 1. **Render** the prompt according to `next.interaction.kind` using the relay rules above. For a native kind, invoke the `function` string from that same `interactions` entry — the Claude Code picker, the Grok picker, and the Codex picker are different names, and substituting one for another is a relay-contract failure. **Always append the progress marker to the rendered question label**: suffix it with ` (<next.progress.label>)` — render `progress.label` exactly as the wizard sent it, never recompute it. Example: label `Step 8/11 · 앞으로 3 스텝 남음` → `Select a model (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. **The rendered question is the last thing you emit in that turn.** Every host streams assistant text in emission order, so an echo, a restatement, a rationale, or a status line emitted after the question call renders *below* the picker and pushes the question away from where the user answers. Say whatever you need to say before the call, then call and stop.
180
+ **One pending interaction per wizard state.** For native interactions, display the question and options only through the selected question tool, not as a second text list. An asynchronous acknowledgement such as `{accepted: true}` means the question is pending, not answered. Wait for the actual user reply; do not repeat the tool call or fetch and render the same step again while waiting. If ending the turn while awaiting a reply, do not restate the question in the final message. Re-prompt only on explicit user edits or a validation error after submitting an actual answer. This is a lead relay instruction; wizard state validation does not deduplicate host UI emissions.
181
+ 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 `$(...)`):
182
+ ```bash
183
+ okstra wizard step --state-file /var/folders/.../okstra-wizard.AbCd.json --answer preprod
184
+ ```
185
+ If the answer contains spaces or shell metacharacters, wrap it in double quotes around the literal string only — never inside `"$VAR"`.
186
+
187
+ **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:
188
+ ```bash
189
+ okstra wizard step --state-file /var/folders/.../okstra-wizard.AbCd.json --answer ""
190
+ ```
191
+ Omitting `--answer` entirely is forbidden. The wizard does not read a missing `--answer` flag as "submit empty": without `--answer` or `--no-submit`, `wizard step` returns `ok: false` naming `--answer` and exits 2, and the step is not submitted. Submitting `--answer ""` is the only way to advance past an intentionally-blank step (e.g. "use phase default").
192
+
193
+ **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.
194
+
195
+ **Enforced:** `tests-js/wizard.test.mjs` pins `parseFlags` on the empty literal (`--answer=` parses to the empty string, a value beginning with `--` is kept) and on the `--no-submit` peek that is the only sanctioned way to see the current prompt without submitting; the Node wrapper (`src/commands/execute/wizard.mts`) is what forwards them to Python.
196
+ 3. **Handle result**:
197
+ - `ok: true` → echo `result.echo` to the user on one short line, then loop with `result.next`.
198
+ - `ok: false` → show `result.error` to the user verbatim, then loop with `result.current` (re-prompt the same step). If `result.current` is `null`, do not loop — the current step cannot render (e.g. corrupt Stage Map); surface the error and stop so the user fixes the plan.
199
+ <!-- END FRAGMENT: wizard-prompt-loop -->
200
+
201
+ The discussion steps the wizard asks, in order: task selection (or task-group and task-id for a new task), task type, then `discussion_start_pick` — "continue this task's discussion" (recommended when the task already has a discussion record), a brief the user already gave, and direct input last — or the `discussion_start` text step. A start input is an http(s) link or a file inside the project (`link`), this task's own key (continue), or anything else (`description`). Then base-ref, the role models (analyser 1–5, recommended 3; report-writer 1; verifiers are chosen at dispatch), and confirmation.
202
+
203
+ <!-- BEGIN FRAGMENT: wizard-confirm-step -->
204
+ ## Step 4: The confirm step's question text is the selection summary
205
+
206
+ When `next.step == "confirm"`, the prompt's `label` already carries the full selection summary (`선택 확인:` followed by one line per resolved input — task-type, task-key, brief, base-ref, worktree, every role slot, directive, related-tasks, clarification, stage, …) and ends with the question. Render that `label` **verbatim, every line, as the question text** of the `confirm` picker (Proceed / Edit / Abort) — the same way you render any other pick step's label. Do not re-author it as a table, do not summarise it into prose, do not drop a line because its value is `(none)`: the user confirms what is on screen, and a line you left out is a setting they never saw (observed: a lead's own table omitted `directive`, `base-ref` and `brief`). Render the picker **as the final output of that turn** — emit nothing after the picker call.
207
+
208
+ `okstra wizard confirmation --state-file <path>` returns the same block as `{ok: true, text: ...}` for a text-only host or when the user asks to see the summary again; it is not a substitute for the label.
209
+
210
+ Keep the provider data-transfer disclosure in that label alongside the selected roles/models and task inputs. The user's actual `Proceed` response authorizes the displayed recipients and task-material scope; it does not grant host execution privileges. When dispatching, follow the host relay's guidance to carry that response and scope into the execution-permission request. A freshly generated `confirmationText` does not retroactively add disclosure to an older user response.
211
+ <!-- END FRAGMENT: wizard-confirm-step -->
212
+
213
+ <!-- BEGIN FRAGMENT: wizard-render-bundle -->
214
+ ## Step 5: Render the task bundle
215
+
216
+ When `next.kind == "done"`, fetch the public wizard outcome:
217
+
218
+ ```bash
219
+ okstra wizard outcome --state-file /var/folders/.../okstra-wizard.AbCd.json
220
+ ```
221
+
222
+ Output: `{ok: true, outcome: {renderArgv: ["--lead-runtime", "...", ...], renderArgs: {...}, orchestration: {chainStages: "..."}, persistActions: [...], confirmationText: "..."}}`.
223
+
224
+ `renderArgv` is the canonical ordered `okstra render-bundle` argument list. Pass its tokens verbatim and in order. Repeated `--role-count` and `--role-model` flags are intentional. `renderArgs` is a compatibility and inspection view only; do not reconstruct an invocation from it. `orchestration` holds signals this skill acts on itself — never pass its entries to `render-bundle`.
225
+
226
+ `userAuthorization`, when present, preserves the confirmation prompt actually emitted by this wizard and the relayed `proceed` response. Its `--user-authorization-json` token carries that record into the run manifest; preserve it unchanged. Do not synthesize a receipt for older confirmed states. Include the translator's disclosed provider/model and report material when citing this record in host execution requests, including `report-finalize`. The record documents task scope; it does not grant host execution privileges.
227
+
228
+ Before retrying a worker in an existing run, inspect its last invocation attempt: await running work; append an attempt only after `failed-no-mutation` within the retry budget. Preserve every other terminal attempt. `okstra team dispatch` routes the legacy declaration-only implementer failure to a separate same-model evidence-recovery invocation; collect its result before independent verification. Other terminal failures need `okstra agent-prompt materialize` with a new `--invocation-id`, distinct `--prompt`/`--result` paths, and unchanged assignment/model for a bounded correction. Do not restart completed implementation or request renewed approval for unchanged scope. Enforcement: `dispatch_core._next_attempt` and `execution_manifest._validate_next_attempt`.
229
+
230
+ Run every `outcome.persistActions[]` entry BEFORE `render-bundle`. The only supported action is:
231
+
232
+ ```json
233
+ {"command":"config.set","key":"pr-template-path","scope":"project|global","value":"<path>"}
234
+ ```
235
+
236
+ Execute the matching command for `scope`:
237
+
238
+ ```bash
239
+ okstra config set pr-template-path "<value>" --scope project
240
+ okstra config set pr-template-path "<value>" --scope global
241
+ ```
242
+
243
+ If an action has an unknown `command`, `key`, or `scope`, stop and report the wizard output instead of inventing a command.
244
+
245
+ Before rendering the next phase's bundle — and between worker rounds within a phase (reverify/critic/gapverify batches), after you have collected that round's results and token usage and before you dispatch the next round — close the panes of the dispatches that finished in the prior round so they do not accumulate: `okstra team reclaim --project-root <projectRoot> --run-manifest <RUN_MANIFEST_PATH>` closes them, records `phase-batch-cleanup panes=<n>` with the number it closed, and prints that `PROGRESS:` line last — emit it as printed and do not call `okstra lead-progress append` for it. The command reads each dispatch's recorded status, so an in-progress worker keeps its pane whichever moment you call it. It closes only the panes okstra opened and recorded — a pane the harness opened for its own teammate carries no recorded id and is not okstra's to close. `shutdown_request` alone only idles the agent and frees no pane, so it stays part of the run-end sequence for roster/token hygiene. A `cli-wrapper` run holds no pane at all and `team reclaim` refuses it, so record the checkpoint there with `okstra lead-progress append … --phase phase-batch-cleanup --field panes=0`.
246
+
247
+ Before you ask the user for any approval, clarification, or decision after workers have been dispatched, run `okstra team reclaim … --gate` first: it closes the finished panes and prints `PROGRESS: phase-gate-cleanup panes=<n>` for you to emit, without recording a batch cleanup. Then `TaskStop` each completed worker. A `TaskStop` by itself idles the task but leaves the pane open — the `team reclaim` call is what closes it. This keeps a user gate from being shown while finished worker panes remain; in-progress dispatches keep their panes. Then follow `prompts/lead/okstra-lead-contract.md` "User confirmation before an approval blocker": read cited plan items, worker findings, and files before asking, and ask in the user's language with each option's outcome.
248
+
249
+ Build the `okstra render-bundle` invocation from `outcome.renderArgv`, passing every token verbatim and in order (including empty strings — they are intentional `use phase default` markers).
250
+
251
+ Before invoking it, follow the active host relay's execution-permission guidance. For Codex, read "Permission before bundle preparation" in the relay already selected at preflight: bundle preparation can write protected Git metadata while creating a branch and worktree, even with `--render-only`. Apply this guidance to the first invocation and each subsequent phase or stage preparation.
252
+
253
+ Analysis sidetracks therefore forward wizard-owned tokens such as `--analysis-target "<value>"` and `--evidence-inputs "<value>"` when they are present. These are examples of the verbatim token rule, not a separate hard-coded argument list.
254
+
255
+ Step 3's empty-answer and escaping rules apply verbatim: every flag in `renderArgv` whose following value is the empty string MUST still be passed explicitly (e.g. `--related-tasks ""`, `--directive ""`) — the wizard's intent is always "flag present with empty value", even where prepare's default happens to be the same empty value.
256
+
257
+ `renderArgv` already contains exactly one `--lead-runtime <host-runtime>` pair. Do not add or replace it. Do not enumerate a fixed provider list in this skill because the wizard and role model pool own the ordered tokens.
258
+
259
+ ```bash
260
+ okstra render-bundle \
261
+ <outcome.renderArgv token 1> \
262
+ <outcome.renderArgv token 2> \
263
+ <each remaining outcome.renderArgv token in order>
264
+ ```
265
+
266
+ `render-bundle` auto-supplies `--workspace-root` and forces `--render-only`. Stdout prints `okstra task root:`, `okstra instruction-set:`, `okstra run manifest:`, and the full rendered lead prompt. Parse the labelled lines for `TASK_ROOT`, `INSTRUCTION_SET_PATH`, and `RUN_MANIFEST_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).
267
+
268
+ Before acting as the lead, read `resources.leadPromptMetadataPath` from the run
269
+ manifest and execute the host-native specification-link gate:
270
+
271
+ ```bash
272
+ okstra agent-prompt record-dispatch \
273
+ --project-root <projectRoot> \
274
+ --run-manifest <RUN_MANIFEST_PATH> \
275
+ --metadata <projectRoot/resources.leadPromptMetadataPath> \
276
+ --enforcement-mode host-native-spec-link-gate
277
+ ```
278
+
279
+ Do not continue from the rendered prompt if this command fails. This record
280
+ associates the current-session lead with a verified invocation specification;
281
+ it does not claim that the host exposed or attested the delivered prompt bytes.
282
+
283
+ The python function underneath allocates the run sequence number and writes `run-context-*.json` under the per-task lock (`~/.okstra/.locks/<task-key>.lock`), then writes `run-inputs-*.json` + all manifests + discovery files outside that lock, and registers the run in `~/.okstra/recent.jsonl` with status `prepared` under the central `~/.okstra/.lock` (a registration failure is reported on stderr and does not fail prepare).
284
+
285
+ **Not enforced:** `tests-js/wizard.test.mjs` pins the argv the wizard produces (`orderedRenderArgv` preserves the Python role selection bytes and repeated role order), not the argv you pass. Prepare's parser defaults most flags to `""` (e.g. `--directive`), so a dropped empty value is indistinguishable from its default and nothing rejects it — passing every token verbatim is on you.
286
+
287
+ You can delete the literal state-file path after this point — its job is done. Invoke `command rm` with the literal path (e.g. `command rm /var/folders/.../okstra-wizard.AbCd.json`), not a shell variable. `command` is what keeps a `rm='rm -i'` alias from turning this into a confirmation prompt nobody is there to answer.
288
+ <!-- END FRAGMENT: wizard-render-bundle -->
289
+
290
+ <!-- BEGIN FRAGMENT: wizard-lead-takeover -->
291
+ ## Step 6: Take over as Okstra lead
292
+
293
+ Read `resources.leadExecutionPromptPath` from the run manifest at `<RUN_MANIFEST_PATH>` — the same manifest Step 5 already opened for `resources.leadPromptMetadataPath` — then read that file verbatim and take over as `Okstra lead` in the current host-native session. Do not construct the path yourself: the prompt is written per run under `runs/<task-type>/prompts/lead-execution-prompt-<task-type>-<seq>.md`, not in the instruction set, and the `<seq>` is assigned at prepare time. The prompt selects exactly one runtime adapter and points to compact intake artifacts first (`active-run-context`, `analysis-profile.md`, and `analysis-packet.md`); full source files such as `analysis-material.md`, `reference-expectations.md`, and `final-report-template.md` are lazy/fallback inputs. Follow the rendered prompt order, do not preempt it.
294
+
295
+ Then proceed through the phases exactly as the lead prompt directs (Phase 1 context → Phase 2+ worker dispatch → final synthesis → final report).
296
+
297
+ Inform the user with one short line:
298
+ > Took over as Okstra lead (`<host-runtime>`) for `<taskKey>` (`<task-type>`). Run dir: `<RUN_DIR_RELATIVE_PATH>`. Beginning Phase 1 (context loading).
299
+ <!-- END FRAGMENT: wizard-lead-takeover -->
300
+
301
+ The rendered lead prompt carries the discussion profile — the facilitator rules (source: `~/.okstra/lib/python/okstra_ctl/phases/discussion/profile.md`). Follow it. The steps below are the loop it describes, with the user-facing choices this skill authors.
302
+
303
+ ## Step 7: Facilitate one research cycle
304
+
305
+ Every `okstra discussion` command takes the full task key and `--project-root <projectRoot>`; the record commands also take `--run-manifest <RUN_MANIFEST_PATH>`. Never edit a file under `<task-root>/discussion/` yourself.
306
+
307
+ 1. Run `okstra discussion status <task-key> --project-root <projectRoot>` on entry and on resume. It prints the phase, the draft version, the one question to put to the user next and any `memoryRegistration` row that is not `registered`.
308
+ 2. Phase `framing`: propose the subject and the question to settle from the start input, and ask the user to confirm them — option 1 your proposal (recommended), option 2 a narrower or broader reading if the start input supports one, last option direct input. Record the answer with `okstra discussion confirm-subject`.
309
+ 3. Run `okstra discussion cycle-open`. On `dispatch`, materialize one `initial` invocation per analyser with the returned `instructionPath` and dispatch them as one batch; then `settle`, Phase 5.5 convergence, and `cycle-close`, as the lead prompt directs. `await-user`, `await-participant` and `await-recovery` mean nothing is dispatched until that wait ends. `present-options` means no new evidence, answer or alternative arrived: show the remaining disagreements and options and ask the user to choose a direction, bring new material, or ask one participant directly. `prepare-run` means the next cycle needs a continuation run (Step 8). `completed` means the user already completed the discussion.
310
+ 4. Put questions to the user one at a time, in the order `status` gives. For each question, read the research results and claims it rests on first, then offer one or two answers the evidence supports (the better-supported one first, marked as recommended) and direct input last. Record the reply with `okstra discussion answer --author user`, and a choice of intent or priority with `okstra discussion decide`. When the user asks one participant directly, record it with `okstra discussion address`, then `question-prompt`, dispatch, and `collect-answer`.
311
+ 5. When `settle` or `collect-answer` reports a failure, show which participant failed and which scope was not verified, then ask: `retry` in this run (recommended when the failure looks transient), `reduce` to continue with the remaining participants, `replace` with another model in a continuation run (not offered for a direct question), and direct input last. Record the choice with `okstra discussion recover`. Never replace a participant without the user's choice.
312
+ 6. Finalize the run as the lead prompt directs. Finalizing a run never completes the discussion; the report carries the draft version until the user confirms completion.
313
+
314
+ ## Step 8: Another research cycle is a continuation run
315
+
316
+ One run holds exactly one research cycle, because a run owns one convergence state. When `cycle-open` returns `prepare-run` — new input arrived after this run's cycle closed, or the user chose `replace` — or when the user wants to research again after new material, finish this run and prepare the next one for the same task:
317
+
318
+ - From the wizard: repeat Step 2 with `--preferred-task-key <task-key>` and pick "continue this task's discussion" at `discussion_start_pick`. Choose the roster again at the role-model steps; keep the previous models unless the user asked to replace one.
319
+ - From the launcher: `~/.okstra/bin/okstra.sh --task-type discussion --project-id <project-id> --task-group <task-group> --task-id <task-id> --discussion-start <task-key>`.
320
+
321
+ Before preparing it, ask the user — option 1 continue now with the same roster (recommended), option 2 continue with the replacement the user chose, last option direct input. The continuation run reads every earlier answer, decision and result under its original author and run.
322
+
323
+ ## Step 9: Completion
324
+
325
+ Completion belongs to the user; agent consensus, a convergence stop or the end of a run is not completion.
326
+
327
+ 1. Record every settled requirement with `okstra discussion add-requirement` (`--applies-to <follow-up label>` when only one follow-up needs it) and every in-scope and excluded item with `okstra discussion add-scope`; record an explicit `none` when nothing is excluded. After `reopen`, the draft already carries the reopened revision's requirements and scope; record only what changed. When the user drops an item, record `okstra discussion withdraw --item <N-NNNN|B-NNNN> --reason "<the user's reason>"`; when the user changes one, withdraw it and add the new one.
328
+ 2. Show `okstra discussion draft <task-key> --project-root <projectRoot>` with the remaining disagreements, the unverified scope and any reduced roster. Ask the user to choose a direction — option 1 the direction the evidence supports best (recommended), option 2 the strongest alternative, last option direct input — and to state the risks they accept in their own words.
329
+ 3. Run `okstra discussion confirm-completion` with the `draftVersion` that `status` printed for the draft the user saw. A refusal names what is missing or says the draft changed: show the current draft and ask again. Never confirm on the user's behalf.
330
+ 4. Read the `memory` field. `registered` needs nothing. `pending` is retried with `okstra discussion memory-sync <task-key> --project-root <projectRoot>`; the revision stays published either way. `refused` names likely secrets: show them, and register again only with the summary the user edited, through `memory-sync --revision <revision-id> --summary-file <path>`.
331
+
332
+ To change a completed discussion, prepare a continuation run (Step 8) and record `okstra discussion reopen --revision <latest revision id> --reason "<why>"` before any other record. The new draft starts with the reopened revision's requirements and scope; ask the user which of them change — option 1 keep them all and change only what the reason names (recommended), option 2 review them one at a time, last option direct input. Earlier revisions stay readable with `okstra discussion revision-show`.
333
+
334
+ ## Step 10: Follow-ups (only when the user asks)
335
+
336
+ A follow-up task starts at `requirements-discovery` in its own run; nothing here starts it.
337
+
338
+ - Write one follow-up's brief with `okstra discussion follow-up-brief <task-key> --project-root <projectRoot> --revision <revision-id> --follow-up <label> --target-task <follow-up task key>` (`--target-project-root <dir>` for another project). Map a requirement to an end state with `--end-state <N-NNNN>=<EB|PB|EO>` only as the user states it. A brief left `reporter-confirmations: pending` needs the user's confirmation through okstra-brief-gen Step 6.5 before its first run.
339
+ - When the follow-up task and this discussion are children of the same `okstra manager` task, run `okstra manager task sync --manager-id <manager-id> --task-group <task-group> --task-id <manager task-id>` afterwards: the child's launch packet then carries the brief and the pinned revision.
340
+ - After a re-discussion publishes a newer revision, show `okstra discussion links` and `okstra discussion impact --from <old> --to <new> --follow-up <label>`, then ask per follow-up — `keep` (recommended while the follow-up's plan is in progress), `adopt`, and direct input last — and record only the user's choice with `okstra discussion apply`. A follow-up that already has a plan is routed to the plan amendment path; nothing here edits a plan or source.
341
+ - A routed follow-up stays `amendment-pending` until the user approves an amended plan for it in the planning path. When `okstra discussion links` shows a `next` that names `complete-amendment`, ask the user — option 1 move the follow-up's brief to the revision now (recommended), option 2 leave it pending, last option direct input — and on option 1 run `okstra discussion complete-amendment <task-key> --project-root <projectRoot> --revision <revision-id> --target-task <follow-up task key>`. Otherwise show what `next` says is missing. Never approve a plan for the user.
342
+
343
+ ## Output Rules
344
+
345
+ - Echo each recorded answer, decision and choice on one short line with its record id.
346
+ - Name every file you show as a markdown link with its absolute path.
347
+ - Close each run with the user's next action: another research cycle (Step 8), completion (Step 9), or a follow-up brief (Step 10), with the reason taken from `status` or `cycle-open`.
@@ -98,6 +98,13 @@ diff and commits: describe only what actually changed. Mark checklist boxes
98
98
  docs box). If the `Commits`/`Diff stat` sections print `-` (empty), tell the user there is nothing to
99
99
  describe and stop. **Never** append AI trailers/footers.
100
100
 
101
+ Open the new-behavior section — `What is the new behavior` in the bundled default,
102
+ or the section of a stored template that describes the change — with a short
103
+ summary of what changed: 2–4 bullets, one change per bullet, each naming the
104
+ observable difference a reviewer would notice. Put the detailed description below
105
+ that summary. The summary restates the details; it adds no claim the diff does
106
+ not support.
107
+
101
108
  ### A3b. Identifier allowlist for the body
102
109
 
103
110
  Reviewers have never seen okstra, so an okstra-internal id reads as a nonsense
@@ -9,7 +9,9 @@ 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/registry.py` (the `STEPS` order) or the owning `scripts/okstra_ctl/wizard/steps_*.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.
12
+ <!-- BEGIN FRAGMENT: wizard-bash-invocation-rule -->
13
+ **Bash invocation rule (permission-friendly)**: every Bash command in this skill begins with the literal token `okstra` (or another already-allowed binary) and passes 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.
14
+ <!-- END FRAGMENT: wizard-bash-invocation-rule -->
13
15
 
14
16
  ## When to Use
15
17
 
@@ -22,6 +24,7 @@ Launch an okstra task — gather inputs interactively via the **wizard state mac
22
24
  - User wants status only — use `okstra-inspect status`.
23
25
  - User wants past runs — use `okstra-inspect history`.
24
26
 
27
+ <!-- BEGIN FRAGMENT: wizard-relay-protocol -->
25
28
  ## How the wizard talks to you
26
29
 
27
30
  Every wizard call returns JSON. The two shapes you'll see:
@@ -62,7 +65,9 @@ The final `confirm` step is a normal `pick` step with three options — `Proceed
62
65
  Never invent additional questions. **Never drop, hide, merge, reorder, or truncate** a `pick` / `pick_group` option — relay every `options[]` entry, including the flagged recommendation. Do not collapse a multi-option pick into a "recommended + Enter directly / Other" shortlist. The wizard's arrays are the complete authoritative choice sets, regardless of the current host UI's usual option limit. The run-prompt recommendation rule (1–2 recommendations + Enter directly) shapes the **option set** only for prompts this skill authors itself, never for wizard-provided options — you may not add, drop, or reorder a wizard option to produce a shortlist. It does not excuse you from recommending: before relaying a wizard step whose answer turns on something readable (the carried report, the sidecar the user already wrote, the prior Stage Map), read it, put what you found in the question body, and name which of the wizard's own options you recommend and why. Relaying a step with no context and no recommendation hands the whole question back to the user — see the lifecycle core contract "Asking the user (BLOCKING)".
63
66
 
64
67
  **One recommendation, shown in one place, and it is option 1.** Wizard labels carry no recommendation marker of their own. The option carrying `recommended: true` is what this run computed, and the wizard already placed it first — except on model-selection steps (`role-models:*`, `role-model:*`), which keep provider display order, so the flagged model may sit anywhere in the list. Append ` (추천)` to that option's label when you render it, and to no other. A checkbox step (`multi: true`) may flag several leading options: they are the recommended set, rendered the same way. Your prose recommendation names one of the flagged options. If you believe a different option is right, do not quietly recommend it in prose while the flagged one still reads as recommended on screen: say you disagree, name both, and let the user pick. When no option carries the flag, the run computed nothing for this step — recommend one from what you read and mark that one, and do not present the first option as a default just because it is first. **Enforced:** `scripts/okstra_ctl/wizard/state.py` `Prompt.__post_init__` refuses a step whose free-input option is not last, whose single-select recommendation is not exactly one option, whose recommendation is not placed first on a single-select step or is not the leading run on a checkbox step (both position checks are skipped on `role-models:*` / `role-model:*` steps), or that marks the free-input / abort escape as a recommendation.
68
+ <!-- END FRAGMENT: wizard-relay-protocol -->
65
69
 
70
+ <!-- BEGIN FRAGMENT: wizard-preflight -->
66
71
  ## Step 1: Preflight
67
72
 
68
73
  Use the launcher's `OKSTRA_RUNTIME_HOST` value when it is present. Otherwise use the registered host ID that the current harness declares for this session. This is a harness capability declaration, never a worker-provider choice. Do not infer it from an executable or `PATH`, and do not silently substitute another host.
@@ -138,6 +143,7 @@ Use this built-in mapping only when the successful preflight response omitted `r
138
143
  ```
139
144
 
140
145
  For numbered interactions, render every option as a 1-based Markdown item containing its label followed by its description verbatim, in original order. For `sequential-group`, ask every question in order and build one compact JSON object keyed by `questions[].step`. Submit each user message unchanged; the wizard owns numbered and grouped normalization.
146
+ <!-- END FRAGMENT: wizard-preflight -->
141
147
 
142
148
  ## Step 2: Initialize the wizard
143
149
 
@@ -155,6 +161,8 @@ For task continuity, pass `--preferred-task-key <project-id:task-group:task-id>`
155
161
 
156
162
  After a successful task selection, retain the returned full task key in this conversation as its task context, including in continuation summaries, and carry it into the next initialization. A different selection replaces it. Selecting a brand-new task clears the previous selection; retain the new full key once its project, group, and task ID are resolved. This conversation carry-forward is a lead instruction, not automatic host-session discovery by the runtime.
157
163
 
164
+ When the user asks for a `discussion` — agents researching a ticket or project with the user to settle its requirements and direction before planning — add `--task-type discussion` to the `init` below. A new task then asks the task type before the brief and takes a start input (a link or a short description) instead of a brief; without the flag a new task asks for a brief first. This is the initialization `/okstra-discussion` runs, so both entries reach the same `render-bundle` and `prepare_task_bundle()` call. A discussion holds one research cycle per run: when its run ends needing another cycle, the next action is `/okstra-discussion` (or this skill again with `--task-type discussion --preferred-task-key <task-key>`), whose start step recommends continuing that task's discussion.
165
+
158
166
  Initialize with the optional preferred key resolved above:
159
167
 
160
168
  ```bash
@@ -172,6 +180,7 @@ Output: the same `{ok, next}` JSON described above. The first `next` is `step: "
172
180
 
173
181
  ## Step 3: Run the prompt loop
174
182
 
183
+ <!-- BEGIN FRAGMENT: wizard-prompt-loop -->
175
184
  Repeat until `next.kind == "done"` (or `"aborted"` — terminal cancel, see "How the wizard talks to you"):
176
185
 
177
186
  1. **Render** the prompt according to `next.interaction.kind` using the relay rules above. For a native kind, invoke the `function` string from that same `interactions` entry — the Claude Code picker, the Grok picker, and the Codex picker are different names, and substituting one for another is a relay-contract failure. **Always append the progress marker to the rendered question label**: suffix it with ` (<next.progress.label>)` — render `progress.label` exactly as the wizard sent it, never recompute it. Example: label `Step 8/11 · 앞으로 3 스텝 남음` → `Select a model (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. **The rendered question is the last thing you emit in that turn.** Every host streams assistant text in emission order, so an echo, a restatement, a rationale, or a status line emitted after the question call renders *below* the picker and pushes the question away from where the user answers. Say whatever you need to say before the call, then call and stop.
@@ -194,6 +203,7 @@ Repeat until `next.kind == "done"` (or `"aborted"` — terminal cancel, see "How
194
203
  3. **Handle result**:
195
204
  - `ok: true` → echo `result.echo` to the user on one short line, then loop with `result.next`.
196
205
  - `ok: false` → show `result.error` to the user verbatim, then loop with `result.current` (re-prompt the same step). If `result.current` is `null`, do not loop — the current step cannot render (e.g. corrupt Stage Map); surface the error and stop so the user fixes the plan.
206
+ <!-- END FRAGMENT: wizard-prompt-loop -->
197
207
 
198
208
  That is the entire interactive flow. The wizard handles:
199
209
 
@@ -202,6 +212,7 @@ That is the entire interactive flow. The wizard handles:
202
212
  - task-type pick (3 options + Enter directly; Enter directly is validated against the full task-type whitelist in a follow-up `text` step). **For a brand-new task the brief is asked first** and the three options are entry phases only (`requirements-discovery` / `improvement-discovery` / `project-analysis` / `feature-analysis` / `change-impact-analysis` / `error-analysis`) — a new task-key has no approved plan, no implementation and no commits, so `implementation`, `final-verification` and `release-handoff` cannot be entered there; the recommended slot comes from the selected brief's `Recommended next phase:` line and falls back to `requirements-discovery` when the brief carries none. For an existing task-key what fills those three depends on `workflow.nextRecommendedPhase` — an object `{phase, status, rationale}`, not a phase-name string. `status: ready` gives the classic trio: its `phase` marked recommended, re-run the current phase, the lifecycle's next step. Any other status contributes nothing at all, since both the recommended slot and the next-step slot derive from that phase — re-run the current phase becomes the first option and the remaining slots fill unlabelled from recently used task-types and then the whitelist. Never recover a phase name from a non-`ready` pointer and propose it yourself: prepare deliberately lowers the pointer to `pending` while its run is unfinished, and the missing recommendation is that signal,
203
213
  - brief path — **for a new task it is asked right after task-group, before the task-type** (it is the only input that says what the task is, and the task-type recommendation reads it); for an existing task it is **asked only for entry task-types (requirements-discovery / error-analysis / improvement-discovery / project-analysis / feature-analysis / change-impact-analysis)** (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). `project-analysis`, `feature-analysis`, and `change-impact-analysis` are brief entry task types. On an existing task a downstream lifecycle 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 of its own — a new-task run answers the brief before the type is known, and the brief is dropped at render time, so prepare always generates the input document that cites the verification report,
204
214
  - analysis-input sub-flow — `project-analysis` has no target or evidence step. `feature-analysis` runs `project_evidence_pick` / `project_evidence`, then `analysis_target_pick` / `analysis_target`; the target is required even when the user skips project evidence. `change-impact-analysis` runs `feature_evidence_pick` / `feature_evidence`, then `project_evidence_pick` / `project_evidence`. The evidence steps show accepted compatible reports first and retain direct-path entry; do not invent a different relation or reorder these steps,
215
+ - `discussion` start input — instead of any brief step, `discussion_start_pick` (continue this task's discussion when it has records, a brief the user already gave, direct input last) or the `discussion_start` text step; prepare turns it into the run's input document,
205
216
  - base-ref pick + git rev-parse validation (skipped when reusing an active worktree),
206
217
  - `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). Implementer slots use role-count / role-model like every other role (`executor` is only a compatibility alias for `implementer`). When an approved plan is selected and a `## PLAN DECISION` sidecar carrying `Status: approved`, 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,
207
218
  - `release-handoff`-only sub-flow: after the approved plan auto-resolves, a `handoff_stage_pick` multi-select — choose the eligible stages to open a PR for, one PR per stage; the result goes out as render-args' `stages` key (csv; empty takes every eligible stage),
@@ -222,6 +233,7 @@ When the selected task type is `project-analysis`, `feature-analysis`, or `chang
222
233
 
223
234
  Do not second-guess the wizard. If the next prompt seems out of place, the bug is in `okstra_ctl.wizard`, not in your interpretation of the user's input.
224
235
 
236
+ <!-- BEGIN FRAGMENT: wizard-confirm-step -->
225
237
  ## Step 4: The confirm step's question text is the selection summary
226
238
 
227
239
  When `next.step == "confirm"`, the prompt's `label` already carries the full selection summary (`선택 확인:` followed by one line per resolved input — task-type, task-key, brief, base-ref, worktree, every role slot, directive, related-tasks, clarification, stage, …) and ends with the question. Render that `label` **verbatim, every line, as the question text** of the `confirm` picker (Proceed / Edit / Abort) — the same way you render any other pick step's label. Do not re-author it as a table, do not summarise it into prose, do not drop a line because its value is `(none)`: the user confirms what is on screen, and a line you left out is a setting they never saw (observed: a lead's own table omitted `directive`, `base-ref` and `brief`). Render the picker **as the final output of that turn** — emit nothing after the picker call.
@@ -229,7 +241,9 @@ When `next.step == "confirm"`, the prompt's `label` already carries the full sel
229
241
  `okstra wizard confirmation --state-file <path>` returns the same block as `{ok: true, text: ...}` for a text-only host or when the user asks to see the summary again; it is not a substitute for the label.
230
242
 
231
243
  Keep the provider data-transfer disclosure in that label alongside the selected roles/models and task inputs. The user's actual `Proceed` response authorizes the displayed recipients and task-material scope; it does not grant host execution privileges. When dispatching, follow the host relay's guidance to carry that response and scope into the execution-permission request. A freshly generated `confirmationText` does not retroactively add disclosure to an older user response.
244
+ <!-- END FRAGMENT: wizard-confirm-step -->
232
245
 
246
+ <!-- BEGIN FRAGMENT: wizard-render-bundle -->
233
247
  ## Step 5: Render the task bundle
234
248
 
235
249
  When `next.kind == "done"`, fetch the public wizard outcome:
@@ -304,6 +318,7 @@ The python function underneath allocates the run sequence number and writes `run
304
318
  **Not enforced:** `tests-js/wizard.test.mjs` pins the argv the wizard produces (`orderedRenderArgv` preserves the Python role selection bytes and repeated role order), not the argv you pass. Prepare's parser defaults most flags to `""` (e.g. `--directive`), so a dropped empty value is indistinguishable from its default and nothing rejects it — passing every token verbatim is on you.
305
319
 
306
320
  You can delete the literal state-file path after this point — its job is done. Invoke `command rm` with the literal path (e.g. `command rm /var/folders/.../okstra-wizard.AbCd.json`), not a shell variable. `command` is what keeps a `rm='rm -i'` alias from turning this into a confirmation prompt nobody is there to answer.
321
+ <!-- END FRAGMENT: wizard-render-bundle -->
307
322
 
308
323
  <!-- BEGIN FRAGMENT: host-orchestration-implementation-planning -->
309
324
  ## Host orchestration rules — implementation-planning
@@ -361,7 +376,10 @@ re-plan it without a RED step. Check `okstra stage-map <task-key>` first: when
361
376
  `okstra stage-close <task-key> --stage <N> --from-commit <sha>` and re-plan only
362
377
  what is left. That command refuses unless the commit exists and the stage's
363
378
  conformance gate permits progress, so it cannot close a stage the run validator
364
- would have blocked.
379
+ would have blocked. A stage whose last row is `failed` only because its checks
380
+ ran in the wrong environment, and whose commit needs no further product edit,
381
+ closes the same way with `--supersede-failed "<reason>"`; the reason and the
382
+ plan path are recorded on the `done` row.
365
383
 
366
384
  If `okstra stage-map` cannot read the prior plan during this planning run,
367
385
  follow the analysis packet's Stage Ledger recovery guidance. A notation error
@@ -434,6 +452,7 @@ A verifier FAIL that a passing lead minor fix answered does not make the verdict
434
452
  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.)
435
453
  <!-- END FRAGMENT: host-orchestration-implementation -->
436
454
 
455
+ <!-- BEGIN FRAGMENT: wizard-lead-takeover -->
437
456
  ## Step 6: Take over as Okstra lead
438
457
 
439
458
  Read `resources.leadExecutionPromptPath` from the run manifest at `<RUN_MANIFEST_PATH>` — the same manifest Step 5 already opened for `resources.leadPromptMetadataPath` — then read that file verbatim and take over as `Okstra lead` in the current host-native session. Do not construct the path yourself: the prompt is written per run under `runs/<task-type>/prompts/lead-execution-prompt-<task-type>-<seq>.md`, not in the instruction set, and the `<seq>` is assigned at prepare time. The prompt selects exactly one runtime adapter and points to compact intake artifacts first (`active-run-context`, `analysis-profile.md`, and `analysis-packet.md`); full source files such as `analysis-material.md`, `reference-expectations.md`, and `final-report-template.md` are lazy/fallback inputs. Follow the rendered prompt order, do not preempt it.
@@ -442,6 +461,7 @@ Then proceed through the phases exactly as the lead prompt directs (Phase 1 cont
442
461
 
443
462
  Inform the user with one short line:
444
463
  > Took over as Okstra lead (`<host-runtime>`) for `<taskKey>` (`<task-type>`). Run dir: `<RUN_DIR_RELATIVE_PATH>`. Beginning Phase 1 (context loading).
464
+ <!-- END FRAGMENT: wizard-lead-takeover -->
445
465
 
446
466
  ## Step 7: implementation unattended chaining (orchestration.chainStages)
447
467
 
@@ -16,6 +16,7 @@
16
16
  --running: #0969da;
17
17
  --done: #1a7f37;
18
18
  --blocked: #cf222e;
19
+ --discussion: #8250df;
19
20
  }
20
21
  @media (prefers-color-scheme: dark) {
21
22
  :root {
@@ -29,6 +30,7 @@
29
30
  --running: #58a6ff;
30
31
  --done: #3fb950;
31
32
  --blocked: #f85149;
33
+ --discussion: #a371f7;
32
34
  }
33
35
  }
34
36
  * { box-sizing: border-box; }
@@ -96,6 +98,7 @@ code.child-key { word-break: normal; }
96
98
  .chip.done { color: var(--done); }
97
99
  .chip.blocked { color: var(--blocked); }
98
100
  .chip.unlinked { color: var(--missing); }
101
+ .chip.discussion { color: var(--discussion); }
99
102
  .task-head { display: flex; flex-wrap: wrap; gap: 8px 16px; align-items: baseline; margin-bottom: 8px; }
100
103
  .counts { display: flex; flex-wrap: wrap; gap: 6px; }
101
104
  .hint { color: var(--muted); font-size: 12px; margin: 6px 0 12px; }
@@ -796,6 +796,11 @@
796
796
  "confirming": "Confirming signal",
797
797
  "rejecting": "Rejecting signal",
798
798
  "next": "Return to comparison"
799
+ },
800
+ "discussion": {
801
+ "outcome": "Discussion outcome",
802
+ "revision": "Canonical revision",
803
+ "draft": "Still a draft: the user has not confirmed completion."
799
804
  }
800
805
  }
801
806
  }
@@ -796,6 +796,11 @@
796
796
  "confirming": "가설을 지지하는 신호",
797
797
  "rejecting": "가설을 반박하는 신호",
798
798
  "next": "구현 방향 재비교"
799
+ },
800
+ "discussion": {
801
+ "outcome": "토론 결과",
802
+ "revision": "정본 확정본",
803
+ "draft": "아직 초안입니다. 사용자가 완료를 확인하지 않았습니다."
799
804
  }
800
805
  }
801
806
  }
@@ -109,8 +109,9 @@ AUGMENTATION_LABELS = {
109
109
  REPORTER_CONFIRMATION_VALUES = {"complete", "partial", "pending", "skipped"}
110
110
 
111
111
  # okstra-manager `task split` renders the same brief contract for each project
112
- # it splits a tracker issue into.
113
- GENERATORS = {"okstra-brief-gen", "okstra-manager"}
112
+ # it splits a tracker issue into; `okstra discussion follow-up-brief` renders it
113
+ # for each follow-up of a discussion revision.
114
+ GENERATORS = {"okstra-brief-gen", "okstra-manager", "okstra-discussion"}
114
115
 
115
116
  SCOPE_VALUES = {"reporter-input", "codebase"}
116
117