---
name: okstra-run
description: Use when the user wants to start or continue an okstra cross-verification task directly from the current agent session. Trigger words include "okstra run", "okstra start", "start okstra", "begin okstra task", "run okstra in this session", "okstra here", "continue on", and "run the next phase".
---

# OKSTRA Run (in-session)

Launch an okstra task — gather inputs interactively via the **wizard state machine** (`okstra wizard ...`), then take over as `Okstra lead` in the current session using the adapter selected for that host.

**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.

**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.

## When to Use

- The user is inside a supported agent host and asks to start an okstra task ("run okstra here", "start an error-analysis on this branch", "okstra implementation-planning for INV-1234").
- Continue an existing task (next phase) without leaving the current agent session.

## When NOT to Use

- User explicitly asks to spawn a new terminal / new agent session — use `okstra-inspect history.4` (resume command) or instruct them to run `okstra` in another terminal.
- User wants status only — use `okstra-inspect status`.
- User wants past runs — use `okstra-inspect history`.

## How the wizard talks to you

Every wizard call returns JSON. The two shapes you'll see:

```json
{ "ok": true, "echo": "task-group: backend-api",
  "next": { "step": "task_id", "kind": "text", "label": "...", "options": [], "echoTemplate": "...",
            "progress": { "index": 5, "total": 11, "remaining": 6 } } }
```

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 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.

```json
{ "ok": false, "error": "approved plan has no APPROVED marker: ...",
  "current": { "step": "approved_plan", "kind": "text", "label": "..." } }
```

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`.

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`.

- `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`.
- `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 ""`.
- `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.
- `numbered-single` → render every option as a 1-based numbered Markdown list and submit the user's next message unchanged.
- `numbered-multi` → render every option as a 1-based numbered Markdown list and submit the user's comma-separated reply unchanged.
- `sequential-group` → render every question in order, preserve each complete option list, collect every answer, and submit one JSON object keyed by `questions[].step`.
- `plain-text` → write `label` as plain text and submit the user's next message unchanged.
- `kind: "done"` → input collection finished; move to Step 5.
- `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).

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.

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.

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.

Never invent additional questions. **Never drop, hide, merge, reorder, or truncate** a `pick` / `pick_group` option — relay every `options[]` entry, 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 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)".

**One recommendation, shown in one place, and it is option 1.** The option carrying `recommended: true` is what this run computed, and the wizard already placed it first — 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 placed first (a checkbox step's recommended options must be its leading run), or that marks the free-input / abort escape as a recommendation.

## Step 1: Preflight

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.

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.

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.

Run one Bash tool call (Bash invocation rule from the top of this file applies):

```bash
okstra preflight --runtime <host-runtime>
```

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=...`.

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.

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.

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.

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.

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.

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.

### Legacy text relay compatibility

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.

```json
{
  "scope": "preflight-response-without-relayContract-only",
  "semanticFunctions": ["plain_text_input"],
  "interactions": {
    "numbered-single": {
      "function": "host-text",
      "input": {
        "question": "label-with-progress",
        "options": "all-in-original-order-as-numbered-markdown-label-and-description"
      },
      "response": { "source": "next-message", "submit": "raw" }
    },
    "numbered-multi": {
      "function": "host-text",
      "input": {
        "question": "label-with-progress",
        "options": "all-in-original-order-as-numbered-markdown-label-and-description"
      },
      "response": { "source": "next-message", "submit": "raw" }
    },
    "sequential-group": {
      "function": "host-text",
      "input": {
        "questions": "all-one-at-a-time-in-original-order",
        "question": "label-with-progress",
        "options": "all-in-original-order-as-numbered-markdown-label-and-description"
      },
      "response": {
        "source": "next-message-by-question-position",
        "submit": "compact-step-json-raw"
      }
    },
    "plain-text": {
      "function": "host-text",
      "input": { "question": "label-with-progress" },
      "response": { "source": "next-message", "submit": "raw" }
    }
  }
}
```

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.

## Step 2: Initialize the wizard

First, generate a state-file path (Bash invocation rule from the top of this file applies to every command below):

```bash
okstra wizard new-state-file
```

This prints one absolute path on stdout (e.g. `/var/folders/.../okstra-wizard.AbCd.json`). Read that path from the tool output and **paste it literally** into every subsequent `--state-file` argument.

Then initialize the wizard with the literal `projectRoot` / `projectId` you parsed from Step 1 and the literal state-file path from above.

For task continuity, pass `--preferred-task-key <project-id:task-group:task-id>` on every initialization when this conversation has a known task. Use the task explicitly requested in the current message first; otherwise use the last task the user selected in this conversation's wizard. Resolve abbreviated task IDs against the project catalog before passing the full key. If the ID is ambiguous, ask which matching task the user means. Never infer this conversation's selection from project-wide `latest-task.json`, catalog recency, or another session. With neither an explicit task nor a prior selection in this conversation, omit the flag. The runtime puts the preferred task first, including a completed task when explicitly carried in, and rejects a key absent from the catalog instead of substituting another task; ordering is covered by `tests/domain/wizard/test_task_selection.py`. The task picker still lets the user change tasks.

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.

Initialize with the optional preferred key resolved above:

```bash
okstra wizard init \
  --state-file /var/folders/.../okstra-wizard.AbCd.json \
  --project-root /abs/path/to/project \
  --project-id   my-project-id \
  --host-runtime <host-runtime> \
  --entry-mode <lead-entry-mode> \
  --available-function plain_text_input \
  --available-function <each-additional-function-in-the-effective-intersection>
```

Output: the same `{ok, next}` JSON described above. The first `next` is always `step: "task_pick"`.

## Step 3: Run the prompt loop

Repeat until `next.kind == "done"` (or `"aborted"` — terminal cancel, see "How the wizard talks to you"):

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 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. **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.
   **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.
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 `$(...)`):
   ```bash
   okstra wizard step --state-file /var/folders/.../okstra-wizard.AbCd.json --answer preprod
   ```
   If the answer contains spaces or shell metacharacters, wrap it in double quotes around the literal string only — never inside `"$VAR"`.

   **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:
   ```bash
   okstra wizard step --state-file /var/folders/.../okstra-wizard.AbCd.json --answer ""
   ```
   Omitting `--answer` entirely is forbidden. The wizard interprets a missing `--answer` flag as "re-emit the current prompt" (a `get-current-prompt` style no-op), not as "submit empty" — so dropping the flag will loop the same prompt forever. Submitting `--answer ""` is the only way to advance past an intentionally-blank step (e.g. "use phase default").

   **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.

   **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.
3. **Handle result**:
   - `ok: true` → echo `result.echo` to the user on one short line, then loop with `result.next`.
   - `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.

That is the entire interactive flow. The wizard handles:

- project report language before task selection: when `.okstra/project.json` has no `reportLanguage` value, ask for a language tag and save the answer to that file. An existing value skips this question. Prompt rendering and progress estimation do not save a language,
- 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),
- 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,
- 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,
- 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,
- base-ref pick + git rev-parse validation (skipped when reusing an active worktree),
- `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,
- `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),
- launch selection after identity/worktree steps: one screen per static role, in profile order. A role that can run several instances (`max > 1`) is a checkbox step `role-models:<role>` (`multi: true`) — the number of models checked is the number of instances, there is no separate count question; the label states the profile range and recommended count, every executable candidate is listed on that one screen (defaults first, the recommended set flagged), and when the list exceeds the host's native checkbox limit the runtime either splits it into several checkbox questions on one `pick_group` screen (interaction plan `native-group`; the question steps are `role-models:<role>#1`, `#2`, … and the answer is one JSON object keyed by them, each value a CSV) when the host's native question group holds every option, or returns the interaction plan `numbered-multi` — render the whole list, never a shortlist or pages. An optional role (`min = 0`, e.g. critic) carries a `추가 안 함` row. A fixed single role (`min = max = 1`, e.g. report-writer) is a single pick `role-model:<role>:1`, and a role capped at one model (`max = 1`, e.g. critic) is a single pick on its `role-models:<role>` step; both split the same way when they exceed the native option limit — the tabs are checkbox questions, the answer is the same keyed JSON object, and the wizard rejects a screen whose tabs together name more than one value. current-session lead is this session and is listed on the confirmation summary, not as a wizard step. The wizard does not fork on defaults-vs-customize, does not show a provider roster multi-pick, and does not offer a separate implementer-provider pick. Dynamic verifiers are not chosen at launch. `--workers` is compatibility-only, not a launch picker. Repeated `--role-count` / `--role-model` tokens on `renderArgv` are intentional,
- **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 role-count·role-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,
- **re-verification scope (`reverify_scope_pick`, `implementation-planning` clarification re-runs only)** — asked right before `confirm` when the re-run is narrowable **or** an answered `C-NNN` traces to no stage. When every answered id traces to a stage: 3 options — `auto` (recommended — leave it to the lead's `okstra incremental-scope` decision) / `full` (re-verify every stage) / Enter directly (a stage-number CSV, validated against the prior report's Stage Map). When an id is unlinked, `auto` is omitted and the user names stages or picks `full`; that unlinked id does not freeze the run at full. The answer goes out as `--reverify-scope` and reaches the lead prompt as the `REVERIFY_SCOPE_MODE` / `REVERIFY_SCOPE_STAGES` tokens; it shapes that CLI's inputs rather than replacing the decision. The confirmation block's `reverify-scope` line names unlinked ids as needing stage numbers, not as a forced full re-run,
- `release-handoff` PR template override + persist scope,
- final `Proceed / Edit` confirmation; on `Edit` the wizard asks which step to rewind to and clears every later answer.

### Analysis sidetrack execution rules

When the selected task type is `project-analysis`, `feature-analysis`, or `change-impact-analysis`:

1. Treat the target project as strictly read-only. No edits, tests, builds, migrations, or deployments are allowed, even if the user asks for verification while starting the analysis run.
2. Pass the wizard's resolved target and evidence values through the render flags exactly as emitted. `project-analysis` emits both values empty; never synthesize an evidence path from a brief or a prior run.
3. A `revision-requested` report is not evidence. The wizard prioritizes its same-task, same-type full rerun and carries the review sidecar through the existing clarification-response channel; preserve that choice instead of substituting a newer report of another type.
4. The rendered lead contract reanalyzes the whole confirmed scope and records a resolution for every affected ID. Do not narrow the rerun to only the disputed rows or mutate the previous report.

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.

## Step 4: The confirm step's question text is the selection summary

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.

`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.

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.

## Step 5: Render the task bundle

When `next.kind == "done"`, fetch the public wizard outcome:

```bash
okstra wizard outcome --state-file /var/folders/.../okstra-wizard.AbCd.json
```

Output: `{ok: true, outcome: {renderArgv: ["--lead-runtime", "...", ...], renderArgs: {...}, orchestration: {chainStages: "..."}, persistActions: [...], confirmationText: "..."}}`.

`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`.

`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.

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`.

Run every `outcome.persistActions[]` entry BEFORE `render-bundle`. The only supported action is:

```json
{"command":"config.set","key":"pr-template-path","scope":"project|global","value":"<path>"}
```

Execute the matching command for `scope`:

```bash
okstra config set pr-template-path "<value>" --scope project
okstra config set pr-template-path "<value>" --scope global
```

If an action has an unknown `command`, `key`, or `scope`, stop and report the wizard output instead of inventing a command.

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, in two passes. First count: `okstra team reclaim --project-root <projectRoot> --run-manifest <RUN_MANIFEST_PATH> --dry-run` closes nothing and prints one `<paneId>\t<kind>` line per pane it would close — count those lines as `<n>`. Then run the same command **without** `--dry-run` to close them, and emit `PROGRESS: phase-batch-cleanup panes=<n>` with that count at the batch boundary. 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, so `<n>` is `0` — still emit the checkpoint.

Before you ask the user for any approval, clarification, or decision after workers have been dispatched, run the same two passes first: `okstra team reclaim … --dry-run` to count the panes, then the same command without `--dry-run` to close them, emit `PROGRESS: phase-gate-cleanup panes=<n>`, and `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.

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).

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.

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.

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. `--workers ""`, `--directive ""`) — `render-bundle` distinguishes "flag absent" from "flag present with empty value", and the wizard's intent is always the latter.

`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.

```bash
okstra render-bundle \
  <outcome.renderArgv token 1> \
  <outcome.renderArgv token 2> \
  <each remaining outcome.renderArgv token in order>
```

`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).

Before acting as the lead, read `resources.leadPromptMetadataPath` from the run
manifest and execute the host-native specification-link gate:

```bash
okstra agent-prompt record-dispatch \
  --project-root <projectRoot> \
  --run-manifest <RUN_MANIFEST_PATH> \
  --metadata <projectRoot/resources.leadPromptMetadataPath> \
  --enforcement-mode host-native-spec-link-gate
```

Do not continue from the rendered prompt if this command fails. This record
associates the current-session lead with a verified invocation specification;
it does not claim that the host exposed or attested the delivered prompt bytes.

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`.

**Enforced:** the same `tests-js/wizard.test.mjs` cases cover the argv this step passes through — `orderedRenderArgv` preserves the Python role selection bytes and repeated role order, so an empty value dropped here changes the rendered bundle and the golden comparison fails.

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.

<!-- BEGIN FRAGMENT: host-orchestration-implementation-planning -->
## Host orchestration rules — implementation-planning

These are the rules the **host orchestrator** follows around an
`implementation-planning` run: when a stage genuinely cannot write a RED step,
and what only the user can grant. They are not lead phase rules — the lead's
rules live in `prompts/profiles/`.

### Step 5.1 (implementation-planning only): user-confirmed TDD bypass offer

Every plan stage must open with a `RED:` step whose outcome is FAIL and reach a
later `GREEN:` step (validator S10c). `tddExemption` waives that, and until
2026-09-10 only for `doc-only`, `config-only`, or `pure-rename` work — so a
stage that is truthfully none of the three had no passable value, and the plan
got through by filing the nearest category. That is the failure this flag
exists to remove: **a closed reason list with no escape makes the plan
misdescribe itself.**

`render-bundle` accepts an optional `--tdd-bypass "<stage>:<reason>"` flag
(implementation-planning only). It records a **user-acknowledged** bypass into
`<task-root>/qa/tdd-bypass.json`, and S10e then accepts that stage declaring
`tddExemption: user-bypass`. The reason is stored **verbatim**.

Offer it only when the run's own output says the stage cannot reach RED — a
planning report blocked at S10e on a `user-bypass` stage, or a plan-body
verification round whose disagreements say the expected FAIL is unreachable
(e.g. the stage worktree HEAD is already the accepted commit). Never offer it
to save a stage that simply has no test written yet; that stage's answer is the
RED step.

This is **never** a lead/worker self-exemption — only the user may grant it,
and the lead has no command that writes this record. Surface it as a 3-option
recommendation picker (per the run-prompt recommendation rule):

1. (recommended) Keep the RED/GREEN requirement — re-plan the stage so its
   first step writes the failing test.
2. Bypass this stage — ask the user for the stage number and reason, then pass
   `--tdd-bypass "<stage>:<reason>"` to `render-bundle` (reason = the user's
   words, unedited).
3. Enter directly — the user types the full `<stage>:<reason>` value.

When the user picks a bypass, append `--tdd-bypass "<stage>:<reason>"` to the
`render-bundle` invocation. Omit the flag entirely otherwise (do **not** pass
`--tdd-bypass ""`). A malformed value aborts `render-bundle` with a
`PrepareError`. The grant is per stage number and per task, and it persists
across runs of that task — the next plan of the same stage may still declare
`user-bypass` until the user's grant is removed from the ledger.

**A stage whose work already landed is usually not a bypass case.** When the
product change is committed and its conformance result is PASS but the stage
never registered as done, the honest fix is to close that stage rather than to
re-plan it without a RED step. Check `okstra stage-map <task-key>` first: when
`doneStages` omits a stage whose commit is on the stage branch, close it with
`okstra stage-close <task-key> --stage <N> --from-commit <sha>` and re-plan only
what is left. That command refuses unless the commit exists and the stage's
conformance gate permits progress, so it cannot close a stage the run validator
would have blocked.

If `okstra stage-map` cannot read the prior plan during this planning run,
follow the analysis packet's Stage Ledger recovery guidance. A notation error
in an existing `dependsOn` cell is work for the new plan, not a reason to ask
the user to edit JSON. Preserve the prior report, existing stage numbers and
completed work; do not close or execute a stage from an unreadable map.
An ambiguous dependency or stage identity remains a blocker.
<!-- END FRAGMENT: host-orchestration-implementation-planning -->

<!-- BEGIN FRAGMENT: host-orchestration-implementation -->
## Host orchestration rules — implementation

These are the rules the **host orchestrator** follows around an `implementation`
run: when to offer a conformance waiver, what a concurrent-run marker means, how
to recover a stale stage SHA, and what the chaining queue does when the next
stage is not ready. They are not lead phase rules — the lead's rules live in
`prompts/profiles/`.

This file is the single source. Two surfaces are generated from it: the
`okstra-run` skill body (marker block, synced by `tools/sync-skill-fragments.mjs`)
and each run's `instruction-set/host-orchestration-rules.md`. Edit here.

### Step 5.1 (implementation only): blocking local conformance waiver offer

`render-bundle` accepts an optional `--qa-waiver "<stageKey>:<reason>"` flag (implementation only). It records a **user-acknowledged** waiver into the task-level conformance manifest entry (`entry.waiver`), letting the run proceed when an `io`-only Tier 3 conformance script genuinely cannot run. The waiver records the user's reason **verbatim**.

Inspect the selected manifest entry's `requires`. If it contains `db`, `http`,
or `external`, it is external-advisory: Do not offer a waiver and continue so
the verifier can attempt automatic startup/execution. A non-PASS outcome will
become a user-owned follow-up. If `requires=[]`, fail closed as declaration or
contract trouble: Do not offer a waiver for `requires=[]`. Offer the existing
waiver picker only when `requires=[io]` and that local command genuinely cannot run.

This is **never** a lead/worker self-exemption — only the user may waive. After classification confirms `requires=[io]`, surface it as a 3-option recommendation picker (per the run-prompt recommendation rule):

1. (recommended) Run the conformance script — no waiver.
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).
3. Enter directly — the user types the full `<stageKey>:<reason>` value.

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`.

### Concurrent-run detection branch (concurrent-run)

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):

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.
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.
3. Enter directly.

### Stale git SHA recovery (git-reconcile gate)

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:

1. Run the `okstra git-reconcile … --check --text` 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.)
2. For each confirm item, present a 3-option picker to the user:
   - **Re-record to the `stage-<N>` branch's current tip (recommended)** — when an intended change such as review feedback lives on that branch.
   - **Enter a different ref directly** — the user names a commit/branch/tag.
   - **Abort** — stop the run without recovering.
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.

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`.

### Next stage not yet ready — normal termination (not an exception gate)
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).

### Stage ended FAIL — stop the queue and report (not an exception gate)
When a stage's synthesised verdict is `FAIL`, Phase 6 writes no carry sidecar and appends a `status:"failed"` row in place of `done` (`prompts/profiles/_implementation-deliverable.md` "Lead post-stage persistence"). **Stop the queue at that stage** and report the failed stage, its report path, and the remaining queue (e.g. `stage 1 FAIL — remaining queue: stage 2, 3, 5; re-enter with okstra-run --stage 1 after the fix`). Do **not** continue to the next stage even when that stage is dependency-independent: an unattended chain that keeps building past a confirmed regression stacks later work on top of it. The `failed` row releases the stage's occupancy, so `--stage <N>` re-enters the same stage on its preserved worktree and branch — there is nothing to unblock by hand.

### Exception gate during chaining
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.)
<!-- END FRAGMENT: host-orchestration-implementation -->

## Step 6: Take over as Okstra lead

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.

Then proceed through the phases exactly as the lead prompt directs (Phase 1 context → Phase 2+ worker dispatch → final synthesis → final report).

Inform the user with one short line:
> Took over as Okstra lead (`<host-runtime>`) for `<taskKey>` (`<task-type>`). Run dir: `<RUN_DIR_RELATIVE_PATH>`. Beginning Phase 1 (context loading).

## Step 7: implementation unattended chaining (orchestration.chainStages)

When `task-type == implementation` and Step 5 outcome's `orchestration.chainStages` 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 ends this queue after same-run recovery is complete; a separately authorized continuous task follows the continuation rule below).

Queue = the topologically-sorted stage list from splitting `orchestration.chainStages` on `,` (the order Task 5 emitted by topologically sorting the dependency closure). For each stage `N` in the queue, in order:

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 blocking local conformance waiver offer·concurrent-run detection·git-reconcile gates apply identically to each stage's `render-bundle`.
2. As in Step 6, become the host-native Okstra 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).
3. After confirming that `done` row was written, close the panes of the stage you just finished: run `okstra team teardown --project-root <projectRoot> --run-manifest <that stage's RUN_MANIFEST_PATH>`. That stage's run is over, so this is the run-end command rather than the round-boundary one. Then move to the next stage. A `status:"failed"` row instead of `done` means the stage ended `FAIL` — close the panes the same way, then stop the queue per "Stage ended FAIL" above.
4. One-line report at each stage start/finish: `stage N start` / `stage N done → next K`.

Once the whole queue is consumed, end the chain and report completion to the user.

The three branches that end or pause the queue — "Next stage not yet ready", "Stage ended FAIL", and "Exception gate during chaining" — are in the host orchestration rules block above.

## Persisting the PR template scope (release-handoff)

Do not read the wizard state file directly. `okstra wizard outcome` exposes any release-handoff PR template write as `outcome.persistActions[]`; execute those actions before `render-bundle`.

## Concurrency

- `prepare_task_bundle` serializes per-task via `~/.okstra/.locks/<task-key>.lock`. Concurrent skill invocations on the same task wait; different tasks proceed in parallel.
- Each wizard run owns its own state file (one per `okstra wizard new-state-file`); two parallel skill invocations do not collide.
- The skill must NOT call `okstra.sh` (or any other bash entrypoint) that would re-implement the orchestration. The wizard + `render-bundle` is the single authority.

## Authorized continuation

At a boundary, distinguish artifact recovery, the next item in the confirmed stage queue, and a new task scope. Continue `recovery.mode: same-run` with its supplied resume command. Continue a confirmed stage queue without asking for another invocation; the wizard receipt's `stageScope` records the displayed selection and prepare rejects stages outside it. For a broader continuous task, cite the user's actual instruction before moving to the recorded ready phase; do not expand a wizard receipt whose task type or inputs no longer match. Preserve approval gates, provider/material scope, and host execution permissions. If these changed or a required user decision remains unresolved, report that specific boundary.

A failed report assembly is not an implementation rejection. Read each skipped step's `blockedBy` value and repair its owning input; do not dispatch workers for derivative missing-file errors. Use the existing retry budget and stop with the first unresolved cause and resume location when an unchanged input produces the same failure without progress. At successful completion inside an authorized continuous scope, report progress and continue; offer `nextCommand` as a user action only when that scope has ended or a concrete boundary prevents continuation.

## Boundary updates

Follow the rendered launch prompt's "Progress, remaining work, and recommendation" guidance at each phase or implementation-stage boundary, at task completion, and before a controlled session pause or handoff. Include results, remaining work, and the recommended action with its reason. During an authorized continuous run, report the boundary and continue the queue; when the lead returns, relay its closeout without repeating an already delivered update.

## Failure Modes

| Symptom | Cause | Fix |
|---|---|---|
| `okstra runtime missing: ...` | First run on this machine, or stale install | `npx okstra@latest install` once, retry. |
| `No module named okstra_ctl.wizard` | Install predates wizard module | `npx okstra@latest install` to refresh. |
| `wizard step` returns `ok: false` repeatedly | User keeps giving invalid answers | Echo the error verbatim and re-prompt the same step — do not advance. |
| `task root not found for <key>` | catalog entry stale or task-key typo | Restart the wizard (`okstra wizard init`) to refresh the pick list. |
| `approved plan is not yet approved (frontmatter ...)` | `implementation` without proper approval | Ask the user to re-run with `--approve` or confirm approval in the in-session wizard, or pick a different task-type. Editing the full reading copy does not approve the plan. |

## Output Rules

- Echo each captured answer (`result.echo`) on one short line so the user sees what was registered.
- Name every file you show the user as a markdown link — `[<what it is>](<path>)`, with the path inside the parentheses. That is the only form the host renders as clickable; a path in backticks is text the user has to copy out. The `report-finalize` result's `reportPaths.markdown` carries the run's report, report record, and team state already in that form. Commands stay in backticks — a link is for a file, not for something to run.
- Never invent identity; if a `text` prompt returns an empty answer where the wizard rejects it, the user must retry.
- After Step 6, begin the lead workflow without re-summarizing the skill itself. For a single run, finish after Step 6 and any same-run recovery, unless the user has already authorized continuing the task through further phases. In an unattended chain where `orchestration.chainStages` 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. When `report-finalize` returns `recovery.mode: same-run`, continue the authorized corrections in this run and execute `recovery.resumeCommand` before closeout; preserve approvals and model choices without reopening the wizard. The command and owner issues are supplied by `report_finalize._finalize_recovery`. When the lead (or this skill, after the lead returns) reports a successfully finalized run over, close with the user's next action — one command they can run now. A prohibition is not a next action. Take the pointer from the `report-finalize` result's top-level `nextRecommendedPhase` (`phase`, `status`, `rationale`; also on stderr as `next phase status:` / `next phase:` / `next phase rationale:`) — do not re-derive it from the report, and treat a `nextRecommendedPhaseError` as "pointer unreadable", said in one line before the `validate-run` branch. The same result also carries `nextCommand` — `{command, note}`, the table below already applied to this run. When `command` is non-empty it is the close; when it is empty the `note` says what to do with the `rationale` instead. After `implementation-planning`, open `blocks: approval` rows → `/okstra-user-response`. A recorded `accept-risk` / `select` / `answer` is not an open blocker. No open approval blocker → `/okstra-run` → `implementation` or `--approve` (do not start another planning run; do not say `/okstra-inspect`). For every other task type, quote the pointer's `rationale` in every branch — that sentence is the report's own reason and it is what the user asked to be analysed. Pointer `status: ready` → `/okstra-run` for that phase; `status: terminal` → say the task is finished, name any follow-up tasks this run registered, and do not say `/okstra-inspect`; `status: blocked` → issue the command the `rationale` calls for (`/okstra-user-response` for the `C-NNN` ids, `/okstra-run` for the phase it names); `validate-run` failed with `recovery.mode: phase-reentry` → name the cause and use `nextCommand` for the recorded earlier phase; otherwise `/okstra-inspect status`.
