# External Lead Runtime Adapter

## Scope

This adapter maps the neutral Okstra lead operations to a generic host. Workers run as Okstra-owned CLI wrapper subprocesses; the host is not asked to provide a pane for them. Read it only when the rendered launch prompt selects `leadRuntime=external`.

## Capability declaration

| Field | Value |
|---|---|
| `runtime` | `external` |
| `leadRoleLabel` | `Okstra lead` |
| `userPromptMode` | `host-text` |
| `workerDispatchBackend` | `cli-wrapper` |
| `initialPromptDeliveryMode` | `eager-include` |
| `sessionAccounting` | `artifact-only` |
| `resumeMode` | `artifact-checkpoint` |
| `teardownMode` | `process-cleanup` |
| `leadEventSource` | `lead-events-jsonl` |

## Wizard interaction relay

Read this contract when `okstra preflight` returns this file as `runtimeReadiness.relayContract`. The JSON is the complete interaction mapping for this host. This relay exposes only text input; do not invent a native picker function.

```json
{
  "schemaVersion": 1,
  "runtime": "external",
  "semanticFunctions": ["plain_text_input"],
  "interactions": {
    "numbered-single": {
      "function": "host-text",
      "input": {
        "questions": "one",
        "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": {
        "questions": "one",
        "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": { "questions": "one", "question": "label-with-progress" },
      "response": { "source": "next-message", "submit": "raw" }
    }
  }
}
```

Render every numbered item as its option label followed by its description verbatim, preserving every item and its original order. The next user message is the raw answer: do not translate a number such as `1`, a CSV reply such as `1, 3`, an option label, or an option value before `okstra wizard step`. For `sequential-group`, collect one raw reply per question in order and build one compact JSON object keyed by the corresponding `questions[].step`; the wizard owns all normalization.


## Semantic operation mapping

| Operation | Mapping |
|---|---|
| `read_artifacts` | Read the manifest-provided paths through the current host's file or shell interface. |
| `write_artifact` | Write only core-authorized `.okstra/` artifacts and preserve their schemas. |
| `prompt_user` | Ask through the host text/question interface and require an explicit approval or clarification response. Emit the question as the last thing in that turn: assistant text emitted after the call renders below the question and separates it from the user's answer. |
| `dispatch_worker` | Verify each materialized invocation, then run deterministic `okstra worker-dispatch --project-root <root> --run-manifest <path>` for CLI assignments. Use `okstra team dispatch` only when the selected pane backend owns visible panes. |
| `await_workers` | Run `okstra team await --project-root <root> --run-manifest <path>` through the host's asynchronous shell facility. |
| `redispatch_worker` | Create the core-specified fresh jobs file and dispatch it with a new `dispatchKind`; never reuse a live worker conversation. |
| `shutdown_workers` | Run `okstra team teardown --project-root <root> --run-manifest <path>` only after the user-approved cleanup gate. |
| `record_lead_event` | Append progress and activity records to the manifest-provided `leadEventsPath`. Use `okstra lead-progress append --phase <phase-id>` for a checkpoint and `okstra agent-activity append --kind <kind>` for an activity record; both resolve the ledger path from the run manifest. Emit the matching `PROGRESS:` line — the command prints it as `progressLine` — and, when an activity record is required, the immediately following `ACTIVITY:` line from the same structured fields. |
| `collect_usage` | Collect artifact/CLI-log-backed usage through the existing Okstra token-usage path; never substitute another runtime's session log. |

## External dispatch details

- For convergence reverify, consume the persisted round plan exactly. This adapter may map and transport each returned batch, but it cannot change batch membership and does not classify findings or branch on task type, provider, or model identity.
- Do not invoke Claude Code team tools or provider-specific LLM transport agents. `okstra codex-dispatch` is a compatibility alias only; use `okstra worker-dispatch`.
- Worker completion is valid only from `workerDispatches[]`, terminal status sidecars, and required Result Paths. Pane creation alone is not completion.
- Reverify uses a fresh jobs file at `runs/<task-type>/state/reverify-jobs-r<N>-<task-type>-<seq>.json`, sets `dispatchKind: "reverify-r<N>"`, and dispatches with `okstra team dispatch --project-root <root> --run-manifest <path> --dispatch-kind reverify-r<N> --jobs-file <jobs-file>`.
- Report-writer uses a fresh one-job jobs file with `dispatchKind: "report-writer"` and the same schema, then dispatches through `okstra team dispatch --project-root <root> --run-manifest <path> --jobs-file <jobs-file>`.
- Generate v2 reverify and report-writer jobs files with `okstra agent-prompt jobs --project-root <root> --run-manifest <path> --dispatch-kind <kind> --metadata <prompt-meta.json> [--metadata <prompt-meta.json>] --out <jobs-file>`. It verifies the inputs and derives canonical identity, role, result paths, and all five digests. Reverify uses role `verifier`; its round belongs to `dispatchKind`. Do not transcribe metadata fields or add `workerId` to v2 files. Existing v1 jobs-file consumers remain available. Report-writer completion uses the narrative and worker-result pointer; Phase 7 later assembles `data.json`.
- After either dispatch, run `okstra team await --project-root <root> --run-manifest <path>` before evaluating terminal status or completion paths.

## Completion, cleanup, and resume

- Await through `okstra team await`; raw Result Path polling is forbidden for this backend.
- Resume from run artifacts and lead-events checkpoints. After usage collection, persistence, and the core user-approval gate, run `okstra team teardown --project-root <root> --run-manifest <path>` and tear down only Okstra-owned panes recorded for the run.
