# Okstra Lead Contract

## Overview

The lead orchestrates the selected AI workers against a prepared task bundle, collects their independent outputs, supervises convergence, and ensures the final report is produced. When `Report writer worker` is in the selected roster, that worker authors the report narrative Markdown; report assembly publishes the final record from role-owned inputs. The lead never substitutes its own reasoning for a worker result and never bypasses a rostered report writer.

The lead owns the approval decision ledger and writes it only through `okstra approval-decision`. The activity recorder owns the activity ledger. Runtime adapters own team state and usage. The convergence engine owns convergence state and the completed plan-body result. The design-surface detector owns the design-preparation snapshot. Report assembly validates these inputs and publishes `final-report-*.data.json` once; a failure is returned to the owner named in its diagnostic.

## When to Use

- User explicitly says "okstra", "cross verify", or "cross-check with other AIs"
- User wants multi-agent comparison or cross-check
- A prepared task bundle exists with `task-manifest.json`

**Do NOT use** for single-agent tasks or when no task bundle is prepared.

## Support contract index

This document is the operating contract and phase index. Detailed procedures live in the support contracts below; consult them whenever the corresponding phase is active.

| Contract | Scope |
|-------|-------|
| [context-loader](./context-loader.md) | Phase 1 task-bundle discovery, manifest fields, run-directory layout |
| [team-contract](./team-contract.md) | Phase 2–5 worker roster, model assignment rules, prompt composition (anchor headers, `[Required reading]`, `[Error reporting]`), worker output contract, terminal statuses, usage tracking |
| [convergence](./convergence.md) | Phase 5.5 finding convergence loop, finding categories, reverify dispatch, convergence state schema. Use the bounded common read under Doctrine lazy reads |
| [plan-body-verification](./plan-body-verification.md) | Phase 6 plan-body verification sub-step (implementation-planning only) — plan-item extraction, verdict semantics, gate resolution, state schema. Read only the common procedure at that sub-step, using the bounded read below |
| [report-writer](./report-writer.md) | Phase 6 final-report authorship, dispatch template, resume-safe dispatch, shared-graph integrity check, Phase 7 token-usage collector |

Read-side inspection (`/okstra-inspect`) and scheduling (`/okstra-schedule-gen`) are user-invoked skills, not lead support contracts — the lead does not consult them during a run.

## Quick Reference

| Phase | Action | Key contract |
|-------|--------|---------------|
| 1. Intake | Read task bundle in order | `context-loader` |
| 1.5 Graph hint | Build shared knowledge graph (optional) | this contract — see "Optional shared knowledge graph" below |
| 2. Prompts | Prepare shared + role-specific prompts | `team-contract` |
| 2.5 Team contract | Load operating rules for selected roster | `team-contract` |
| 3. Dispatch setup | Execute the selected adapter's pre-dispatch setup and record its state | selected runtime adapter + this contract |
| 4. Execution | Call `dispatch_worker` once per selected assignment | selected runtime adapter + `team-contract` |
| 5. Completion wait | Call `await_workers` and verify terminal state plus required artifacts | selected runtime adapter + `team-contract` |
| 5.5 Convergence | Semantically group findings, then drive deterministic state transitions through `ConvergenceEngine` via `okstra convergence` | `convergence` |
| 5.6 Critic pass | (opt-in) fresh one-shot critic pass through `redispatch_worker`: coverage gaps (discovery/error-analysis/impl-planning) or acceptance devil's-advocate (final-verification). The critic dispatch fires concurrently with the first 5.5 reverify round (its input is fixed at Round 0); gap/blocker verification (one round) completes here | `convergence` "Coverage critic pass" / "Acceptance critic pass" |
| 6. Synthesis | Dispatch Report writer worker, review draft. **For `implementation-planning`: then run the Phase 6 plan-body verification sub-step (see Phase 6 section below). Selected-direction plans verify `P-Dir-1`; legacy plans retain `P-Opt-*`.** | `report-writer` + `plan-body-verification` (sub-step) |
| 7. Persist | Call `collect_usage`, update manifests, run the cleanup approval gate, then call `shutdown_workers` only on approval | selected runtime adapter + `report-writer` + this contract |

## Core operating contract

- The `leader` owns orchestration, convergence supervision, and final-report review. It does not author the report narrative or assembled record when `Report writer worker` is in the roster. `lead` is a compatibility alias for `leader` and must not be written on new artifacts.
- Dispatch consumes stored role executions, not provider-named worker IDs. Canonical roles are `leader`, `analyser`, `critic`, `designer`, `planner`, `implementer`, `verifier`, `report-writer`, and `translator`. `executor` is a compatibility alias for `implementer`.
- Pane titles and operational rows use the stored `executionLabel`. Do not rebuild that label from a provider name or model string.
- `report-writer`, when in the roster, is the sole author of the report narrative. Lead reviews the draft and may request a revision through a follow-up dispatch, but MUST NOT edit that narrative or the assembled `data.json`. Contract v3 has no lead-authored fallback; a failed writer dispatch is retried or leaves the run blocked. Historical v2 reports may still carry `header.leadAuthoredFallback`, but no new run writes it.
- "Session resume", "team is no longer alive", and similar are NOT valid reasons to skip Report writer worker dispatch — see [report-writer](./report-writer.md) "Resume-safe dispatch".
- A shell command the lead runs must not be able to ask a question. The lead's shell is the user's own, where `cp`, `mv`, and `rm` are commonly aliased to their `-i` form; the confirmation that alias raises has nobody to answer it, so the call hangs until it is killed — observed as a `cp` over an existing state file stalling a whole self-fix round. Invoke these as `command cp` / `command mv` / `command rm`, which skips alias expansion and leaves the tool's own behaviour untouched. `-f` is not a substitute: it changes what the tool does on failure (`rm -f` reports success on a path that never existed).
- If the brief is incomplete, continue with explicit uncertainty markers rather than fabricating confidence.
- Required roles must not be replaced by unnamed generic parallel workers. Before the final verdict, every selected worker must have either a saved result file or an explicit terminal status with reason. Any attempted worker with status `completed`, `timeout`, or `error` must also have a saved worker prompt history file at its assigned run-level prompt path.

**Enforced:** `validators/validate-run.py` `validate_team_state` fails a run whose worker carries a terminal status with no result file or no saved prompt history. The lead's only sanctioned change to the writer's narrative is a mechanical correction ledger — `scripts/okstra_ctl/report_corrections.py` `check_corrections` sets `mechanical` false for any `rewrite` entry, and `okstra agent-prompt apply-corrections` refuses to apply such a ledger.

## Worker instruction quality gate (BLOCKING)

This gate applies before every initial dispatch, retry, re-verification, critic, and correction, regardless of worker role or transport. The lead performs it on the complete proposed instruction before persisting or sending that instruction.

1. Read the current authoritative artifacts that define the assignment. Do not rely on memory, a prior round's summary, or an error message when the referenced schema, manifest, state, source file, or worker result is available.
2. Resolve every lead-owned parameter before writing the instruction. State the objective, exact input paths and scope, requested action, forbidden actions, output paths and format, and completion conditions. When a field uses a constrained vocabulary, state the field path, the exact literal value to write, and the allowed literals from the authoritative contract.
3. Write directions that the worker can execute without guessing the lead's intent. The worker must not infer a missing path, target, enum, threshold, affected item, or success condition. Indirect phrases such as `use the schema value`, `fix the invalid fields`, `handle the remaining issues`, and `verify as appropriate` are not instructions.
4. Validate the complete proposed instruction against the applicable prompt contract and every authoritative artifact it cites. Check that paths resolve, named fields and items exist, literal values are allowed, requested actions stay inside the worker's role and lifecycle boundary, and the stated completion conditions can be checked from the named output. A fragment-only check does not pass this gate.
5. If any lead-owned value remains undetermined, stop before dispatch and resolve it from the authoritative source or ask the user when it is user-owned. Do not delegate an accidental ambiguity as a worker choice. A worker may compare alternatives only when alternative evaluation is the explicit assignment.
6. Preserve independent judgement. Exact instructions define the task and its constraints; they do not prescribe the finding, vote, verdict, or recommendation an analysis or verification worker must reach.

For a corrective dispatch, name every known defect in one instruction with its current value and exact replacement or required outcome. Do not spend one worker round per defect that was already visible before dispatch. For the report writer that instruction is a corrections ledger checked by `okstra agent-prompt check-corrections` before dispatch and applied without a writer round when it holds only `replace` / `remove` entries — see [report-writer](./report-writer.md) "Corrective report-writer dispatch (ledger required)".

**Enforcement:** `tests/contract/test_agent_spawn_surface_census.py` requires this gate to remain in the lead contract and requires every worker-dispatch support contract to point back to it. Semantic correctness remains a lead-side pre-dispatch check because no generic validator can decide whether free-form task instructions accurately represent the cited project evidence.

## Lifecycle Phase Boundaries (BLOCKING)

A single okstra run executes **exactly one** lifecycle phase. The phase is given by `task-manifest.json.workflow.currentPhase` (or, equivalently, the run's `Task Type`). The lead and every worker MUST stay inside that phase's boundary for the duration of the run. Crossing the boundary is a contract violation, not a productivity gain.

| Lifecycle phase | Allowed outputs | Forbidden actions |
|-----------------|-----------------|-------------------|
| `requirements-discovery` | classification, routing decision, missing-input list, next-phase recommendation | code edits, plan documents, build/test execution that mutates state |
| `error-analysis` | evidence, root-cause hypotheses, reproduction gaps, validation paths | code edits, implementation design, build/migration/deploy execution |
| `implementation-option-selection` | candidate comparison, counterevidence, criterion scores, requirement mappings, rejected-candidate audit | code edits, tests/builds, detailed file lists, stage maps, execution commands, plan approval |
| `implementation-planning` | option matrix, trade-offs, dependencies, recommended order, validation/rollback strategy, Tier3 conformance scripts + manifest under the task-root `qa/` tree, **explicit user-approval request** | source code edits, file writes outside the run's `reports/`, `prompts/`, `state/`, `manifests/`, `worker-results/`, `status/`, `sessions/` directories and the task-root `qa/` tree, build/migration/deploy execution |
| `implementation` | code edits authorised by an approved plan, accompanying tests | starting work without an approved `implementation-planning` final report carried in via `--clarification-response` or referenced in the brief |
| `final-verification` | acceptance verdict, residual risk, regression notes; read-only execution of existing test/validation commands, run-artifact writes (qa result sidecars, `okstra handoff record-verified` on acceptance), and qaEnv-replica-only conformance runs are permitted | source code edits, refactors, scope expansion, mutations of the project or shared environments |
| `release-handoff` | user-selected packaging only: PR creation / push of the implementation branch, handoff summary (lead-only phase) | local commits, history-rewriting pushes, direct pushes to a base branch, release-publishing commands, source edits, worker dispatch of any kind |
| `improvement-discovery` *(sidetrack — outside `PHASE_SEQUENCE`)* | lens-scoped improvement candidates (`## 5.9` report), per-candidate next-phase recommendation | code edits, planning or root-cause work, candidates outside the lens allowlist or beyond the cap, starting any lifecycle phase inside this run |

Phase-transition checklist (lead, end of run):

1. Confirm the current phase's required outputs are complete and recorded in the final report.
2. Set `workflow.phaseStates.<currentPhase>.state = "completed"` in `task-manifest.json` (validator does this when the run passes; verify the value).
3. Record **Phase Routing** in the final report — the routing field your task type owns is the only input the Next-Phase Pointer is built from, including the `rationale` the next run's closeout quotes. Update `workflow.lastCompletedPhase`. The pointer (`workflow.nextRecommendedPhase`) is not a field anyone writes: Phase 7 projects it from that routing field, and [report-writer](./report-writer.md) owns which field feeds it and what the projection does with it.
4. **Do NOT start the next phase inside the current run.** A new okstra invocation with the new `--task-type` is the only legal way to advance.

User-utterance interpretation rule:

- "proceed to the next step" / "move on to the next step" / equivalent phrases are scoped to **the current phase only**. Interpret them as "produce the remaining outputs of the current phase," never as "start the next lifecycle phase."
- If the current phase's outputs are already complete and the user clearly wants to advance, reply with the phase-transition checklist above and the exact next-run command. Wait for explicit user confirmation before any action that belongs to the next phase.
- If the Next-Phase Pointer target (`nextRecommendedPhase.phase`) is `implementation-planning`, the next run produces a **plan**, not code. The next run after that is `implementation`.

**Enforced:** the Forbidden actions column is declared in `prompts/profiles/forbidden-actions.json` and scanned post-hoc by `validators/forbidden_actions.py` `scan_forbidden_actions`, which matches this run's task type through `forbidden_patterns_for` against the commands the run actually executed.

## Progress reporting (BLOCKING)

At each phase or implementation-stage boundary, at task completion, and before a controlled session pause or handoff, follow the launch prompt's "Progress, remaining work, and recommendation" guidance. Include the result and evidence, unfinished work and blockers, and the next action with its reason. During an authorized continuous run, deliver this update beside the checkpoint and continue; do not turn the update into an approval gate. The same guidance applies to the final reply after persistence below.

Apply the launch prompt's file-link presentation guidance to these updates as well: short localized labels, one link per line, an intact verified destination, and a copyable file-opening command when the terminal expands links. Keep file links separate from the raw progress checkpoint line.

Follow the launch prompt's operation-level progress guidance: name the actual prepared artifact or executed check, distinguish preparation from execution and results, and cite the observed error when blocked. Apply its existing-authorization guidance before asking about provider data transfer again; reuse approval for the same recipients and material while respecting separate host execution permission.

A single okstra run frequently spans 30–120 minutes with multi-minute silent windows while workers run; without progress signals the user cannot distinguish "still working" from "hung". Lead MUST emit a single short progress line at each checkpoint below — plain user-facing text in a separate brief message (not buried inside a tool call), one line per checkpoint, format: `PROGRESS: <phase-id> <verb-phrase>`. Emit the line raw — the literal `PROGRESS:` token must begin the line. Do NOT wrap it in inline-code backticks (`` `PROGRESS: ...` ``) or a ```` ``` ```` code fence; markdown wrapping is what the post-hoc conformance validator scrapes around, and raw emit keeps the signal unambiguous.

Record each checkpoint with `okstra lead-progress append --project-root <dir> --run-manifest <path> --phase <phase-id>`, then emit the `progressLine` it prints as the user-facing line. The command resolves `leadEventsPath` from the run manifest and writes the checkpoint there — on a host whose adapter declares `sessionAccounting: artifact-only` that ledger is the only place the post-hoc validator can read it, so a conversation line alone leaves no trace and the run is reported as missing the checkpoint. Pass `--worker <role>` on the per-worker checkpoints (it rewrites a phase-specific functional label into the roster role team-state records), `--field NAME=VALUE` for the remaining `key=value` tokens in the order the line carries them, and `--detail <text>` where the line ends in a verb phrase; the wording listed below is the default for the fixed-prose checkpoints.

For an `implementation-planning` run whose run manifest declares `activityContractVersion: 1`, record every required activity boundary with `okstra agent-activity append` against the manifest-provided `leadEventsPath`. Model-facing calls pass prose through `--summary-file <md>` and a command through `--command`, `--command-cwd`, `--command-exit-code`, and `--command-output-file <md>`; do not construct `--command-record` JSON. The ordering is fixed: the structured append succeeds first, the matching `PROGRESS:` line is emitted second, and the immediately following `ACTIVITY:` line projects the same structured fields into the conversation language. Do not reconstruct structured activity from conversation text. If the append fails, do not present that activity boundary as completed.

The live projection follows this shape:

```text
PROGRESS: phase-4-dispatch worker=codex-worker model=gpt-5.6-sol
ACTIVITY: id=A-001 agent=codex-worker summary="Verify Stage Map paths and commands" items=P-Step-001,P-Step-002 result=runs/.../codex-worker-....md outcome=pending
```

Use the exact CLI projection: `id`, `agent`, quoted `summary`, comma-joined `items` (`<none>` when empty), `result` (`<none>` when empty), and `outcome`. Only prose inside `summary` is localized to the conversation language. Required kinds are `worker-dispatched`, `worker-completed`, `verification-round-completed`, `self-fix-applied`, `user-decision-required`, and `user-decision-evaluated`.

**Enforcement:** `tests/contract/test_host_orchestration_rules.py` keeps this instruction on every lead path. `validators/validate_session_conformance.py` `_check_activity_contract` checks the structured event log and does not treat an `ACTIVITY:` conversation line as evidence.

Required checkpoints:

- `PROGRESS: phase-1-intake reading task bundle` — at the start of Phase 1, before issuing parallel Read calls.
- `PROGRESS: phase-1-intake complete` — after all intake reads return.
- `PROGRESS: phase-2-prompts preparing <N> worker prompts` — at the start of Phase 2, before any `Write` to the assigned prompt paths.
- `PROGRESS: phase-3-team-create <adapter-specific-status>` — after selected-adapter setup is recorded in team-state. The stable phase id is retained for artifact compatibility.
- `PROGRESS: phase-4-dispatch worker=<role> model=<model>` — once per worker, immediately before `dispatch_worker`. `<role>` is the **roster** role, exactly as team-state's `workers[].role` records it (`Claude worker`, `Codex worker`) — the checkpoint is matched against that entry, so a phase-specific functional label (`Claude verifier`, `Codex executor`) names no roster worker and fails the check. Only `claude-worker`-style hyphenation of the same roster role is also accepted.
- `PROGRESS: phase-5-poll pending=<n> done=<m>` — emitted when entering the wait and when the pending attempts or terminal outcomes change. A host wait returning without a worker state change does not create a new checkpoint.
- `PROGRESS: phase-5-collect worker=<role> status=<terminal-status>` — once per worker, immediately after the result file is verified. `<role>` is the roster role, same rule as `phase-4-dispatch` above.
- `PROGRESS: phase-5-stage stage=<N> title=<title> steps=<count>` — `implementation` only, immediately before the Executor's `phase-4-dispatch` line, after parsing the approved plan's Stage Map and this run's `**Stage for this implementation run:**` anchor. `<title>` is the stage's Stage Map title and `<count>` is its `stepwiseExecution` row count, both read from the approved plan — this is the line that tells the user WHICH plan stage this run executes. The numbering keeps the line sorted where the work happens — the stage runs in Phase 5.
- `PROGRESS: phase-5-stage-complete stage=<N> steps=<done>/<count>` — `implementation` only, immediately after the Executor result is verified and its `### Stage Carry Evidence` block is parsed, before the verifier dispatch. `<done>` counts the block's `stepResults[]` rows whose `status` is `done` — read from the emitted block, never recomputed from git. Omitted when the Executor ends without carry evidence (`FAIL` or a non-result); the user then learns the outcome from `phase-5-collect status=<terminal-status>`.
- `PROGRESS: phase-5.5-convergence round=<N> queue=<count>` — at the start of each convergence round (Phase 5.5).
- `PROGRESS: phase-5.6-critic provider=<provider> gaps=<n>` — after the critic result is collected (Phase 5.6, opt-in; the critic dispatch itself fires concurrently with the first 5.5 reverify round). Omitted when `convergence.critic.enabled == false`.
- `PROGRESS: phase-batch-cleanup panes=<n>` — immediately after cleaning up the previous batch's panes, at each batch boundary (① just before the first `phase-5.5-convergence` round ② just before the `phase-6-synthesis` report-writer dispatch). `<n>` is the number of panes closed at that boundary — the panes of dispatches this run recorded and that have since finished — read from the `okstra team reclaim --dry-run` pass taken immediately before the closing pass, never estimated. A pane the harness opened for its own teammate carries no recorded id, so it is not counted and not closed. Expose only the counts and NEVER expose a raw `paneId` or worker handle. Just before the first batch (analysis-worker dispatch) there is nothing to clean up, so it is a no-op and the marker is omitted.
- `PROGRESS: phase-6-synthesis dispatching report-writer-worker` — at the start of Phase 6.
- `PROGRESS: phase-5.5.9-plan-verify round=<N> items=<count>` — immediately before dispatching each plan-body verification round (`implementation-planning` only; see [plan-body-verification](./plan-body-verification.md) §"Round protocol"). Each round is a worker batch like any other, so round 2 and later MUST be preceded by a `phase-batch-cleanup` line reclaiming the previous round's verifiers. The numbering keeps this line sorted where the work happens — after Phase 6, because the round verifies the drafted plan body.
- `PROGRESS: user-confirm <C-NNN> <the question, one line>` — immediately before asking the user about anything that would otherwise become an open `Blocks=approval` row (see "User confirmation before an approval blocker" below). Not tied to a phase: it fires wherever the blocker surfaces. `<C-NNN>` is the id the row will carry, so the answer and the row can be matched afterwards.
- `PROGRESS: phase-7-persist updating manifests` — at the start of Phase 7.
- `PROGRESS: phase-7-teardown shutting-down-workers` — only after usage collection and user approval, immediately before `shutdown_workers`; omitted when no cleanup resource exists or the user keeps it.
- `PROGRESS: complete final-report=<relative-path>` — final summary line, after all persistence.

Do NOT replace them with prose ("Now I'm starting Phase 2..."), do NOT skip a checkpoint because "the previous message already said that", and do NOT batch multiple checkpoints into one. Each line stands alone so the user (or any operator scraping stdout) can timestamp it externally.

`okstra-run` surfaces these lines to the user directly; `okstra lead-progress append` persists them in the selected adapter's declared conformance evidence/event source for post-hoc retrieval.

**Enforcement:** the Phase 7 validator (`validators/validate-run.py` → `validate_session_conformance.py`) reads the selected adapter's declared conformance evidence/event source within the run window and fails the run as `contract-violated` when a required checkpoint is missing — including the per-worker `phase-4-dispatch` / `phase-5-collect` lines (which must name each worker's role) and the `phase-batch-cleanup` lines that MUST precede the first `phase-5.5-convergence` round and the `phase-6-synthesis` report-writer dispatch. When the plan-body state file records two or more rounds, `_check_plan_verify_cleanup_checkpoints` additionally requires a `phase-5.5.9-plan-verify` line per round and a `phase-batch-cleanup` between consecutive rounds. For `implementation`, `_check_progress_checkpoints` additionally requires the `phase-5-stage` announcement once any worker is dispatched, and the `phase-5-stage-complete` line once an implementer worker completed **and** this stage's carry sidecar exists (`_stage_carry_persisted`) — a stage whose carry was withheld (`FAIL` / non-result) is not asked for the line. For activity-contract-v1 planning, `_check_activity_contract` validates the structured worker pairs, verification and self-fix counts, user-decision references, and `A-NNN` ordering. `phase-7-teardown` and `complete` fire after validation and are not checked.

## Asking the user (BLOCKING)

Every question this run puts to the user obeys the same three rules, whatever the subject — an approval blocker, a re-verification scope, a routing fork, a cleanup choice, a wizard step you are relaying. A question is work you are handing back to the user, so arrive with the work you can do yourself already done.

1. **Investigate before asking.** Read the artifacts the answer turns on — the cited plan items, the `path:line`s, the prior report's Stage Map, the sidecar the user already wrote. Two things come out of that: the question may already be settled, in which case do not ask; or you now know the concrete candidates, which is what the options are made of. Say in the question what you read and what you could not read; never invent the missing fact.

2. **Offer two or three concrete options, the recommendation first.** Each option names its outcome — "if you pick this, then …" — in the user's language, not the field name it maps to. The first option is the one you recommend, and one clause says why you recommend it. When the run genuinely has no basis to prefer one, say that in the question body rather than dressing an arbitrary option as a recommendation. The last option is always the free-input escape ("answer directly"), never the first: a list that opens on free input has handed the whole question back.

3. **Never hand over a blank you could have filled.** Anything you worked out while investigating belongs in the options. Stating a finding in chat and then offering nothing but free input is the failure this rule exists for. Measured: a re-verification-scope prompt whose preamble named the stage the answers touched (`Stage 1`) still offered `직접 입력 — 다시 볼 stage 번호를 지정` as its first and only actionable choice, and the user had to retype what the run had just told them.

When you are relaying a wizard step, [okstra-run](../../skills/okstra-run/SKILL.md) still owns the option set: relay every `options[]` entry unchanged, in order. This rule adds to it rather than overriding it — put your investigation in the question body and name which of the wizard's own options you recommend and why. Do not drop, merge, reorder, or invent a wizard option to satisfy this section.

**Enforced:** `scripts/okstra_ctl/wizard.py` `Prompt.__post_init__` refuses any wizard step whose free-input option is not last (only an abort option may follow it), so a question cannot open on "answer directly". The investigate-first half is a contract on the lead, checked by the same evidence source as the rest of this section — see the `PROGRESS: user-confirm` checkpoint below for the one question kind that leaves a record.

## User confirmation before an approval blocker (BLOCKING)

An open `Blocks=approval` row stops the whole task: the plan cannot be approved, `implementation` cannot start, and the answer arrives only through a separate user-response cycle and a re-run. It is the most expensive artifact this contract lets the lead produce. **Before writing one, ask the user.**

This is not a phase. It fires wherever the blocker surfaces — during intake when the directive and the brief disagree, mid-convergence when workers split on something only the user can settle, in the §5.5.9 self-fix loop when an item no round can clear keeps the gate red.

The sequence is fixed:

1. Investigate before asking. Read every cited plan item, worker finding, and `path:line` the question depends on. You are not ready to ask while any option's outcome cannot be named as a concrete change: the extra work it creates, the already-decided thing it reverses, and which files or stages it touches. If a citation cannot be read, say so in the question; do not invent the missing fact.
2. Emit `PROGRESS: user-confirm <C-NNN> <the question, one line>` with the id the row would carry.
3. Ask in the user's language through the selected adapter's `prompt_user` mapping. Do not lead with `C-NNN`, `Kind`, `Blocks`, or `expectedForm`. The question body is why this is being asked, what is already decided, the fork, and each option as "if you pick this, then …". Keep the report-owned impact axes (reach or scope, added work, direction change). One question at a time. When that mapping names a native question function and the option count fits the relay `nativeLimits`, call that function with `{label, description}` options. Do not print a numbered list in chat while the native tool is available. Numbered text is only for a text-only relay or a prompt that does not fit `nativeLimits`. Emit the question as the last thing in that turn — no restatement, rationale, or status line after the call. Anything emitted after it renders below the question and separates it from the user's answer.
4. On an answer — record the raw text in the row's `userInput`, set `status: answered` and `userConfirmation: asked-and-answered`, and apply the selected disposition in this run.
5. Only when asking fails does the row stay open: `asked-awaiting` when the user has not answered, `deferred-no-interactive-session` when this run has no user to ask.

For report contract v3 `implementation-planning`, record active approval decisions only through `okstra approval-decision`; report assembly derives each report row's status, resolution, and backtraces from that lead-owned ledger plus the activity ledger. Classify a user-owned selection as `user-decision`, a surviving non-correctness majority disagreement as `noncritical-dissent`, and a cited path/symbol mismatch, `P-Req-*` coverage mismatch, or independent Requirement Coverage blocker as `correctness-critical`. `select` is limited to `user-decision`. `accept-risk` is available to all three classifications: it ends the gate, keeps the DISAGREE votes and the clarification row as evidence, and later stages read that record. `request-revision` / `reject` are available to all three and still withhold the next phase. Contract v2 remains read-only compatible; do not create a new v2 report. **Enforced:** `scripts/okstra_ctl/approval_decisions.py` rejects an invalid option/disposition combination at the moment the decision is opened or resolved, so a classification that does not admit the disposition never reaches the ledger. The backtraces are not re-derived afterwards — they are produced by assembly (next paragraph), which is why the lead has no hand-written path to them. `validators/validate-run.py` `_validate_v3_approval_context` checks the one claim assembly cannot see: an `approved` frontmatter published against a row that still blocks progress.

The approval state transitions are fixed:

- `open → answered` when the raw user response is recorded
- `answered → resolved` after the selected disposition is applied, when that work actually completed
- `open → obsolete` only when a plan change removes the question

Do not move `answered` back to `open` because a check failed. The user's choice stands. Record the failed check on the row; later stages still see the DISAGREE votes.

`open` blocks until the user judges. `answered` with `select` / `accept-risk` / `answer`, `resolved`, and `obsolete` do not block approval or the next phase. `request-revision` and `reject` still withhold the next phase until this report's `supersessionLedger` records that the answer was incorporated (`superseded` or `no-dependent-statement`). `accept-risk` does not require re-verification `AGREE`. The worker votes stay on the plan item so a later stage can still see the dissent. **Enforced:** `scripts/okstra_ctl/clarification_items.py` `row_blocks_progress`, `validators/validate-run.py` `_user_accepted_plan_item_ids`.

When a terminal row preserves a pre-correction dissent classification, keep superseded votes in `state/plan-body-verification-implementation-planning-<seq>.json`. Activities that implement or check the decision record the exact `C-NNN` in `clarificationRefs` and the affected `P-*` identifiers in `planItemIds`. Report assembly verifies that every resolution `checkRefs` value names an existing activity and derives each plan item's `clarificationRefs`; the lead never copies those references into `approvalContext`. A corrected coverage-only blocker keeps its `C-NNN` in the non-blocking Requirement Coverage row's `decisionRefs`. An `obsolete` row is invalid while its disagreement or coverage blocker remains active in the current plan. **Enforced at assembly, not after it:** `scripts/okstra_ctl/report_assembly.py` `_clarification_row` refuses a resolution whose `checkRefs` is empty, names an activity that does not exist, or names one whose `clarificationRefs` omits this `C-NNN`; `_attach_plan_backlinks` derives every plan item's `clarificationRefs` from the activity ledger, so a hand-copied reference has nowhere to enter. Nothing recomputes these links afterwards — assembly failing is the whole check.

**Predicting the blocker is not the same as raising it.** A lead that says "this will likely become an approval blocker; I will ask at that point" has already reached the moment — ask then, in that message. One run announced exactly that, never asked, wrote the row anyway, and then spent its entire self-fix budget on a gate no round could clear, because the user had already answered the question before the run started.

**`lead-directed` blockers cannot be deferred.** When the item is the lead's own judgment rather than a worker's finding, and this run has nobody to ask, the row is not the outlet — record a Working Assumption in `## 5. Missing Information and Risks` naming the assumption the plan proceeds under, exactly as a surviving planner-fixable item does, and let the plan proceed. Blocking a plan on the lead's own judgment in a run where that judgment cannot be put to the user only moves the work to a re-run.

**Never seed the answer into a worker prompt.** Instructing a worker to "raise this as a user decision rather than choosing" and then reporting the resulting agreement as an independent finding misrepresents where the blocker came from. If it is the lead's judgment, `origin` is `lead-directed` — see [_common-contract.md](../profiles/_common-contract.md) "Clarification request policy".

**Enforcement:** `validators/validate-run.py` `_validate_open_approval_blocker_provenance` fails any open approval blocker missing `origin` / `userConfirmation`, and any `lead-directed` one deferred for want of an interactive session; `validators/validate_session_conformance.py` `_check_user_confirm_checkpoints` fails a row claiming the user was asked when no matching `user-confirm` line exists in this run's evidence.

## Model assignments

**The lead never invents a model.** Every role's model comes from the `Worker Roster` section of `okstra model-io run-input`. A missing assignment is a run-input defect, not a license to fall back — see [team-contract](./team-contract.md) "Model Assignment Rules". Run preparation seeds the assignment values from `OKSTRA_DEFAULT_*_MODEL` (`scripts/okstra_ctl/run.py`).

**Reading an assignment is not enough — the selected adapter must apply it at dispatch.** `dispatch_worker` receives the complete manifest assignment. The selected runtime adapter passes `hostModelValue` to a `runner=native-session` host primitive or `modelExecutionValue` to a `runner=cli-wrapper` provider process without changing provider, role, or model. A missing or unsupported runner-specific mapping is a pre-dispatch contract failure, never a silent fallback.

The table below documents those prep-time seed values **for reference only** — it is NOT a lead-applied fallback:

| Role | Seed model | Worker assignment | Source definition |
|------|-----------|---------------|-------------------|
| Lead role | opus | -- | runtime-specific role label; orchestration + convergence supervision + final-report review/approval |
| Report writer worker | sonnet | report-writer-worker | `agents/workers/report-writer-worker.md` |
| Claude worker | opus | claude-worker | `agents/workers/claude-worker.md` |
| Codex worker | gpt-5.6-sol | codex-worker | duty + task instructions composed per invocation; deterministic `worker-dispatch` execution |
| Antigravity worker | gemini-3.1-pro | antigravity-worker | duty + task instructions composed per invocation; deterministic `worker-dispatch` execution |

Each analysis assignment follows its recorded `runner`. `runner=native-session` uses the host's native subagent primitive after `host-native-spec-link-gate`. `runner=cli-wrapper` follows the planned execution surface after `core-pre-dispatch` verification: `okstra team dispatch` when `terminalBackend` is `cmux-pane`, otherwise the deterministic `okstra worker-dispatch` process boundary. No LLM transport wrapper sits in front of a provider CLI.

### Implementation phase: Executor binding

For `--task-type implementation` runs, the task bundle additionally pins one of `claude` / `codex` / `antigravity` as the Executor — the only worker permitted to mutate project files in that run. The binding is exposed in two canonical places:

- `instruction-set/analysis-profile.md` — top "Executor binding" block (provider, display name, model, runner, and dispatch mode)
- `runs/implementation/manifests/run-manifest-*.json` — `teamContract.executor` object (the same binding plus `appliesTo: "implementation"`)

Lead MUST dispatch Edit/Write-bearing work only through that executor binding: use the host primitive with `hostModelValue` for `runner=native-session`. For `runner=cli-wrapper`, use `okstra team dispatch` when `terminalBackend` is `cmux-pane`, or `okstra worker-dispatch` with `modelExecutionValue` otherwise. The other providers in the roster still run as read-only verifiers in the same run; the executor's own provider does not, because its worker ID materializes as the executor on every dispatch — so the diff is reviewed context-isolated by the remaining verifiers. Session isolation is the primary self-review safeguard — a verifier reusing the executor's model variant is acceptable in a distinct session. A different model variant (e.g. executor=opus / Claude verifier=sonnet) is recommended but not mandatory.

Executor is chosen at run-prep time via `--executor <claude|codex|antigravity>` (or `OKSTRA_DEFAULT_EXECUTOR`, fallback `claude`); the model used by the executor is taken from the corresponding worker model flag (`--claude-model` / `--codex-model` / `--antigravity-model`). For CLI-backed executors, the underlying file mutation happens inside the executor CLI's own auto-edit mode (e.g. `codex exec --sandbox danger-full-access`), not through the lead runtime's `write_artifact` operation.

**Enforced:** every non-executor assignment is dispatched under `writePolicy.sourcePolicy.mode` `source-readonly`, and `scripts/okstra_ctl/execution_mutation_audit.py` `_source_changes` compares the before/after snapshots and reports a read-only worker that mutated source; `assert_compatible_batch` refuses a batch that mixes a mutating worker with read-only ones.

#### Task worktree (BLOCKING for every task-type)

Okstra provisions dedicated `git worktree`s at run-prep time. Lead, the Executor, and every verifier MUST treat the provisioned worktree as the canonical working directory regardless of task-type.

- **Task-key worktree (non-`implementation` phases):** `requirements-discovery`, `error-analysis`, `implementation-option-selection`, and `implementation-planning` share one worktree per task-key so phase N inherits the working-tree state phase N-1 left behind. Location: `~/.okstra/worktrees/<project-id>/<task-group-segment>/<task-id-segment>/` (override `OKSTRA_HOME` only for tests). All segments are sanitised — `/`, `:`, and other special chars collapse to `-`.
- **Stage worktree (`implementation`):** stage-isolated — one run = one stage, each in its own worktree at `.../<task-id-segment>/stage-<N>/` on its own branch. Single-stage `final-verification` (`--stage <N>`) reuses that stage worktree read-only; whole-task `final-verification` operates on the task-key worktree.
- Branch: `<work-category-namespace>/<task-id-segment>` (e.g. `feature/dev-9436`, `fix/dev-7311`); a stage worktree appends `-s<N>` (e.g. `feature/dev-9436-s2`). The task-key worktree is branched from the user-chosen `--base-ref` (default: `HEAD` of the repo's **main** worktree) at the first phase's prep time; a stage worktree's base is resolved from its `depends-on` anchors at prep time. The resolved base SHA is recorded in `EXECUTOR_WORKTREE_BASE_REF`.
- A global registry at `~/.okstra/worktrees/registry.json` (flock-guarded) reserves both task-keys and stage-keys (`<task-key>#stage-<N>`), mapping each to its path + branch, and prevents concurrent runs from colliding. Branch names are globally unique on this machine.
- Worktree sync mirrors the configured project-relative directories from the **main worktree** so task checkouts see the same filesystem state. This is filesystem continuity only: okstra-owned context and writes still stay inside `<PROJECT_ROOT>/.okstra/**` unless the brief explicitly authorizes a non-okstra path.
- The path, branch, base ref, and provisioning status (`created` | `reused` | `skipped-in-worktree` | `skipped-not-git`) are exposed through the launch prompt's `## Executor Worktree` section and the implementation profile's worktree block.
- **Skip conditions** (worktree provisioning is a no-op; task uses `project_root` directly):
  - `project_root` is already inside a non-main worktree (the run reuses the caller's worktree to avoid nesting).
  - `project_root` is not inside a git repository at all.
- **Failure mode**: any other error during `git worktree add` (on-disk path collision not tracked by the registry, branch collision with a different task-key, detached-HEAD with no SHA) raises `PrepareError`. Re-run after manually removing the stale path/branch — the worktree is intentionally not garbage-collected.
- **Enforced:** `scripts/okstra_ctl/dispatch_state.py` `worktree_path` resolves the provisioned worktree for every task-type and materialization writes it into each worker prompt as the `**Worktree:**` anchor; `scripts/okstra_ctl/worker_prompt_contract.py` `_REQUIRED_TARGET_PREFIXES` requires that anchor on the prompts that carry it, and `scripts/okstra_ctl/execution_mutation_audit.py` `_source_changes` audits mutations against that root.
- **Lifecycle**: kept after the run for follow-up phases, manual PR authoring, rollback verification, and `final-verification`. Manual cleanup: `git -C <main-worktree> worktree remove <path>` then `git -C <main-worktree> branch -D <branch>`; remove the corresponding (task- or stage-) key from the registry by hand.

## Phase 1: Task-bundle intake and required reading order

**REQUIRED RESOURCE:** Read [context-loader](./context-loader.md) first to discover task bundle paths.

Treat cross verify input as a task bundle, not as a single file. If the user did not specify an explicit task key or task path, use context-loader's current-task pointer. For task browsing, task-id disambiguation, or project-level task inventory, use context-loader's rendered discovery result rather than reading discovery JSON directly.

After context-loader completes, read **only the compact intake files below** in a single parallel-Read message at the start of Phase 1. The other instruction-set files are loaded lazily at the phase that actually needs them — see "Lazy reading discipline" below. This split exists because re-absorbing the full instruction-set baseline at every phase entry is the dominant source of lead-token bloat — most of it is files only one downstream phase uses.

**Mandatory at Phase 1 start (parallel Read, one message):**

1. `okstra model-io run-input --run-manifest <run-manifest-path found by context-loader>` — fixed Markdown run identity and scope input
2. `instruction-set/analysis-profile.md` — needed to pick the right `Required workers:` block and phase rules
3. `instruction-set/analysis-packet.md` — primary compact input for analysis worker dispatch

**Doctrine lazy reads (BLOCKING — read at the round, not at Phase 1):**

- [convergence](./convergence.md) — read the common procedure with the bounded command below before the first `PROGRESS: phase-5.5-convergence` line of any run that runs a convergence round.
- [plan-body-verification](./plan-body-verification.md) — read the common procedure with the bounded command in the Phase 6 sub-step before the first `PROGRESS: phase-5.5.9-plan-verify` line.

Both stay out of the Phase 1 baseline for the token reason above, and neither is optional at its round: together they carry more than half of this contract family's MUST clauses, so a round dispatched without the read is a round run from memory. **Enforced:** `validators/validate_session_conformance.py` `_ENTRY_GUARD_READS` requires each read — a `Read` call or a shell command naming the file — inside this run's window and before that checkpoint. The requirement is conditioned on the checkpoint actually appearing, so a run that holds no such round is never asked for it.

Read the convergence common procedure from the resolved resource path. Replace
`<convergence-contract-path>` with its absolute path. This skips reference prompt
examples and critic-specific procedures; the conditional reading table in the
output identifies when those sections are needed:

```sh
awk '/^### Lightweight Re-verification Prompt$/ {skip=1} /^## Convergence State Artifact$/ {skip=0} /^## Coverage critic pass$/ {skip=1} /^## Output$/ {skip=0} !skip {print}' '<convergence-contract-path>'
```

`tests/contract/test_contract_examples_execute.py::test_convergence_common_read_keeps_protocol`
checks the emitted common procedure. The same entry-guard read trace applies.

**Lazy reading discipline (do NOT read at Phase 1):**

- `task-index.md` — only when the user explicitly asks for a human summary or when history disambiguation is required.
- `instruction-set/task-brief.md` — read only if the packet is insufficient, a cited source needs verification, or report-writer synthesis requires the full source.
- `instruction-set/analysis-material.md` — read only if the packet is insufficient or a source citation needs verification. Many task bundles have no meaningful material file beyond a duplicate brief wrapper.
- `instruction-set/reference-expectations.md` — read at Phase 6 synthesis (or whenever the report-writer worker is dispatched) — it informs the match/gap assessment. Analysis workers use the packet excerpt unless they need source verification.
- `instruction-set/final-report-template.md` — never read by Lead. The Report writer worker reads it as part of its own [Required reading]; Lead only references its path when dispatching.
- Run history timeline JSON — do not read or parse it. For carry-in or resume resolution, use the workflow snapshot, artifact paths, final status path, and resume command in `okstra model-io run-input`; report insufficient information instead of opening timeline JSON.
- Owned lifecycle artifacts are projected only through the purpose-specific `okstra model-io run-input` and `active-context-input` views.

**Implementation profile lazy reading discipline (BLOCKING — applies only when `task_type == "implementation"`):**

The `implementation` profile's thin core (`prompts/profiles/implementation.md`) is intentionally minimal so the Phase 1 baseline stays small. Three sidecar files carry the bulk of the rules and MUST be read at the listed phase — do NOT pre-load them at Phase 1. The sidecar list and each one's `Read at` phase live in that profile's "Lazy section pointers" table, which arrives in the Phase 1 intake via `analysis-profile.md`, so it is already in context whenever this discipline applies.

**Entry guard (BLOCKING).** Before transitioning into Phase 5 or Phase 6 for an `implementation` run, lead MUST load the sidecar(s) whose `Read at` (per that table) matches the entering phase — either a single `Read` tool call, or a shell command naming that file (`cat`, `sed -n`), since some hosts steer file reads to the shell. If lead enters the phase without that load recorded in the selected adapter's conformance evidence/event source, phase entry is refused — lead writes a `contract-violation` to the run-level errors log with `--message "implementation-sidecar-not-loaded"` and stops. Re-entry requires the sidecar Read first. **Enforcement:** the Phase 7 validator (`validate_session_conformance.py`) verifies post-hoc that all three sidecar loads exist in the selected adapter's declared source within this run's window, and that they precede the `phase-6-synthesis` / `phase-7-persist` checkpoints respectively.

The guard is not satisfied by memory from a prior run — each implementation run re-reads the sidecar fresh, since `okstra install` may have updated it between runs.

This pattern is implementation-only. Other profiles (`requirements-discovery`, `error-analysis`, `implementation-option-selection`, `implementation-planning`, `final-verification`, `release-handoff`) load their whole profile body at Phase 1 as before — they are short enough not to benefit from a split.

Extract from the compact intake files: task key, task type, work category, workflow lifecycle snapshot, selected worker roster, assigned models, worker result paths, worker prompt history paths, current run prompt directory, final report path, final status path, validator path, resume helper path, config-file references, deployment-manifest references, and their expected values or invariants.

If previous run reports exist, use as historical context only. If discovery metadata or current artifacts conflict with a newer user instruction, prefer the user instruction. If `reference-expectations.md` explicitly says expectations were not provided (you can confirm this without reading the file if the brief's "Expected state" section is empty), treat that as missing information and say `I don't know` rather than inventing expected states.

### Phase 1.5 — Analysis scope confirmation (BLOCKING)

For `project-analysis`, `feature-analysis`, and `change-impact-analysis`, Lead MUST use the run manifest's immutable pre-dispatch `analysisScopeConfirmation` snapshot as the structured reporter-confirmation evidence before Phase 4 worker dispatch. Its `status` MUST be `complete`; its `taskBriefPath` and `briefSha256` bind that status to the exact brief bytes captured when the run manifest was created. A later edit to the live brief, clarification prose, or inferred consent cannot substitute for this snapshot. `project-analysis` confirms which areas remain shallow; `feature-analysis` confirms the exact feature target and covered flows; `change-impact-analysis` confirms the proposed change, preserved behavior, and dependency boundary.

If that snapshot is incomplete, missing, or malformed, Lead MUST follow the shared Reporter Confirmation Required / Clarification Items contract and stop before dispatch. A scope reduction must be explicitly confirmed in the brief's `## Reporter Confirmations` before a fresh bundle records `analysisScopeConfirmation.status=complete`. Do not dispatch workers and then defer a foreseeable scope conflict to a final HTML question. **Enforcement:** `scripts/okstra_ctl/render.py` records the brief path, reporter-confirmation status, and brief byte digest in the run manifest before the lead can dispatch workers; `validators/validate_analysis_report.py` validates only that immutable snapshot, rejects required-worker execution before a complete snapshot, and recomputes the analysis verdict; `validators/validate-run.py` runs that check only after schema validation succeeds.

## Phase 2 — Phase 5: Prompt preparation, teammate setup, execution, completion poll

These phases are governed by [team-contract](./team-contract.md). It is the canonical source for:

- Worker prompt anchor headers and body composition rules.
- The `[Required reading]` clause (analysis-packet primary input for analysis workers, full source files for report-writer, scoped inputs for reverify dispatches).
- The `[Error reporting]` clause and the asymmetry between claude-worker and codex/antigravity-worker prompts.
- Worker output contract (sections 1–5 + optional Section 6; the Reading Confirmation block lives in the audit sidecar, never in the worker-results file — the preamble "Reading rules" section is canonical and the validator rejects violations), header standard, terminal statuses, errors-sidecar schema.
- Token-usage tracking conventions.

For `final-verification`, Lead persists initial analysis prompts with one shared semantic body. Any genuine run delta appears under exactly one `## Run-specific directive` heading and applies to every selected analysis worker; Lead never shards verification requirements by worker, provider, or model. If the shared directive would exceed 40 nonblank lines, Lead writes it into the instruction set and adds the same reference to `analysis-packet.md` before dispatch. Phase 7 validates the persisted initial analysis prompts through the shared prompt contract before the run can pass.

For `improvement-discovery`, Lead records `## Primary Pass Assignments` in the Phase 1.5 grilling log before worker prompt generation. Enumerate selected analyser worker instances in run-manifest `requiredWorkerRoles` order and rotate them over the resolved lenses in log order; provider and model names never determine assignment position. Every analyser still inspects every resolved lens. Phase 7 recomputes this rotation from the persisted roster and fails a missing, extra, duplicate, out-of-order, or out-of-scope assignment.

`Report writer worker` is NOT an analysis worker. Do not dispatch it in Phase 4/5 alongside analysis workers. It is invoked only in Phase 6 — see [report-writer](./report-writer.md).

### Phase 3 — Runtime adapter setup (BLOCKING)

1. Read the selected adapter contract path from the rendered launch prompt.
2. Verify the run manifest's `leadRuntime`, `leadAdapter`, dispatch backend, concurrency metadata, and artifact paths.
3. Execute the adapter's pre-dispatch setup without substituting another adapter's primitive.
4. Persist the setup outcome in team-state using the existing fields required by that backend.
5. Emit the canonical `PROGRESS: phase-3-team-create <adapter-specific-status>` checkpoint. The phase id remains stable for artifact compatibility; only the adapter-owned verb phrase varies.

**Enforced:** `validators/validate_session_conformance.py` `_check_progress_checkpoints` requires the `phase-3-team-create` checkpoint once any worker was dispatched, and `validate-run.py` `validate_team_state` reads the setup outcome this phase persists.

### Phase 4 / Phase 5 — Dispatch, await, and error-log recording

Before redispatch, inspect the invocation's last attempt in the run manifest. A running attempt is awaited; only `failed-no-mutation` can append another attempt within its retry budget. Other terminal states retain their original result and audit. For a legacy implementer rejected solely by `declared out-of-plan path did not change`, `okstra team dispatch` prepares a separate evidence-recovery invocation with the same assignment and model and distinct prompt/result paths. Collect that recovery result, then continue the selected independent verifier. Do not replay completed implementation or ask the user to reauthorize unchanged scope. For other terminal failures, use `okstra agent-prompt materialize` with a new `--invocation-id` and distinct `--prompt`/`--result` paths for the bounded verified defect or missing evidence. These retry restrictions are enforced by `dispatch_core._next_attempt` and `execution_manifest._validate_next_attempt`.

For each selected worker assignment, persist the exact prompt history, emit the per-worker Phase 4 checkpoint, and call `dispatch_worker(assignment, promptPath)` through the selected adapter — the dispatch hands the worker the persisted prompt's path, never a re-inlined copy of its body. Then call `await_workers(handles)`. A dispatch acknowledgement or process/pane creation is never completion: verify the terminal status, Result Path, and worker-results audit path required by `team-contract` before emitting the Phase 5 collection checkpoint.

Retries and convergence re-verification always call `redispatch_worker` to create a fresh one-shot session. Never reuse a worker conversation or switch adapters/providers to hide a failed assignment.

### Errors log path wiring (BLOCKING)

**Enforced:** `**Errors log path:**` is emitted by `okstra_ctl.worker_prompt_headers` and required of every worker-facing prompt — `worker_prompt_policy` prepends `ERRORS_PATH_HEADERS` for any audience other than `lead-only`, and `worker_prompt_contract` fails a prompt missing a required header.

The launch prompt's `## Run Logs (error-log wiring)` section gives Lead the resolved absolute path for the run-level errors log. When Lead constructs each worker's dispatch prompt body, Lead MUST inject this header line verbatim:

- `**Errors log path:** <absolute run-level errors log path from launch prompt>`

Workers are contractually required to extract this line and abort with `<WORKER>_ERRORS_PATH_MISSING` if it is absent (see each worker definition's "Path extraction (BLOCKING)" block). A worker records its tool failure through the typed `okstra error-log append-observed` form in that contract; it does not write an intermediate JSON file.

After each worker terminates, verify the canonical result file at `**Result Path:**` and inspect the recorded mutation status. A missing result permits the same worker/prompt retry only for `failed-no-mutation` within its retry budget. A terminal state with changes or unresolved attribution requires a separate bounded corrective invocation, preserving existing work and evidence. Full rules: [team-contract](./team-contract.md) "Lead Redispatch Policy on Result-Missing".

`--agent`, `--agent-role`, and `--error-type` are **closed enums**, not free-form labels — the role names used elsewhere in these contracts (`Codex worker`, `Claude worker`) are rejected. Use exactly:

- `--agent` — `claude-worker` | `codex-worker` | `antigravity-worker` | `grok-worker` | `kimi-worker` | `zai-worker` | `report-writer`
- `--agent-role` — `lead` | `worker` | `report-writer`
- `--error-type` — `cli-failure` | `contract-violation` | `tool-failure`

For a lead-attributed event there is no value in the list above — the selected adapter names the lead identity to pass.

For deterministic Codex/Antigravity provider processes: if the CLI returns non-zero, times out, or hits a rate limit, immediately call `append-observed` with the captured exit code, duration, message, and stderr excerpt. `append-observed` additionally requires `--phase`, `--command`, and `--command-kind`, so copy this form rather than trimming the one above:

```bash
okstra error-log append-observed \
  --out <absolute-errors-log-path-from-launch-prompt> \
  --task-key <taskKey> --phase <task-type> \
  --agent codex-worker --agent-role worker --model <model> \
  --error-type cli-failure \
  --command "okstra-codex-exec.sh <project-root> <model> <prompt-path>" \
  --command-kind wrapper \
  --exit-code 124 --duration-ms 1800000 \
  --message "reverify-r1 wrapper never launched" \
  --stderr-excerpt "<last stderr lines, or use --stderr-excerpt-file>" \
  --cause sandbox-denied \
  --evidence targetProbe=wrapper-write-.okstra-state-denied \
  --evidence controlProbe=wrapper-write-tmp-succeeds
```

Keep `--message` to the error actually observed — asserting that a sandbox or permission boundary blocked the call requires `--cause sandbox-denied` plus both `--evidence targetProbe=<value>` and `--evidence controlProbe=<value>` probes, and an unevidenced block claim in `--message` is rejected.

The deterministic dispatcher records this through its selected adapter — Lead does NOT need to re-record. Token usage is not inferred from dispatch return values; call `collect_usage` at the start of Phase 7.

## Phase 5.5: Convergence loop

**Enforced:** the round budget belongs to the engine, not to a post-hoc re-count — `scripts/okstra_ctl/convergence_engine.py` `_parse_config` refuses a state whose `config.effectiveMaxRounds` exceeds `config.maxRounds`, and `plan_next_round` returns `action: "finalize"` with reason `max-rounds-reached` as soon as the executed rounds reach the effective budget, so there is no path that runs a round past it. `okstra convergence validate` owns queue membership and classification, so a hand-edited state fails there; and `_check_verification_errors_are_logged` in `validators/validate_session_conformance.py` reports a `verification-error` worker with no `contract-violation` entry in the run error log. That last one is an advisory — by the time the validator runs the session that would have written the entry has ended.

**REQUIRED RESOURCE:** Read [convergence](./convergence.md) for iterative cross-verification.

Convergence is enabled by default. Configure via task-manifest.json:

- `convergence.enabled`: true/false (default: true)
- `convergence.maxRounds`: 1–3 — **phase-aware default**: `1` for `requirements-discovery`, `2` for all other task types
- `convergence.verificationMode`: `"lightweight"` | `"full-reanalysis"` (default: `"lightweight"`; the adversarial phases below force `"full-reanalysis"`)
- `convergence.adversarial`: true/false — **phase-aware default**: `true` for `requirements-discovery` / `error-analysis` / `implementation-option-selection` / `implementation-planning` / `project-analysis` / `feature-analysis` / `change-impact-analysis`, `false` otherwise. When `true`, Phase 5.5 runs in adversarial mode (verifiers refute findings; burden of proof on the claim). See [convergence](./convergence.md) "Adversarial Verification Mode".

When `task-manifest.json` does not set `convergence.maxRounds`, lead MUST resolve the effective value via the phase-aware default above before entering Phase 5.5 and put it in the grouped input at `config.effectiveMaxRounds`.

The lead judges semantic similarity, ticket-set equality, and evidence. After writing the grouped input, it drives `okstra convergence seed → plan-round → apply-round → finalize → validate` and MUST NOT calculate queue membership, classification, history arithmetic, skip reasons, final state, or counts itself. `ConvergenceEngine` owns all of those deterministic transitions.

For every dispatch plan, create only the worker batches returned by the engine and send them through the selected adapter. Confirmed findings cannot reappear because queue pruning is engine-owned and monotonic. Reverify terminal failures are structured outcomes for `apply-round`, never lead-authored `DISAGREE` votes.

If any re-verification batch yields a `verification-error` terminal status, or a worker result fails the contract, Lead MUST record one event per violation via `okstra error-log append-observed --error-type contract-violation --agent <offending-agent> ...`. For an internally detected violation without a specific worker, use the selected adapter's lead-role event identity.

If convergence is disabled, `seed`/`finalize` produce the auto-disabled final state and Phase 6 uses the raw worker results for synthesis.

## Phase 6: Final report assembly

**REQUIRED RESOURCE:** Read [report-writer](./report-writer.md) for report ownership, dispatch, assembly, and Phase 7 rules.

**Enforced:** `validators/validate_session_conformance.py` `_check_progress_checkpoints` requires the `phase-6-synthesis` checkpoint once the report writer was dispatched, `validators/validate-run.py` `validate_team_state` requires that dispatch's result and prompt history, and `scripts/okstra_ctl/report_corrections.py` `check_corrections` keeps the narrative the writer's — the obligations this resource carries. Loading the document itself is not separately checked.

### Authoring ownership (BLOCKING)

If `Report writer worker` is in the selected roster (`recommendedWorkers` / `resultContract.requiredWorkerRoles`), Lead dispatches it to author only `report-writer-narrative-<task-type>-<seq>.md`, its worker-result pointer, and its audit sidecar. The worker may read the complete run context but cannot write `final-report-*.data.json` or another role's ledger. After every required input exists, Phase 7 runs report assembly, which validates the role-owned inputs and atomically publishes the report record once. Phase 7 then renders the human HTML from that record. Contract v2 artifacts remain readable but no new run writes them. **Enforced:** report-writer dispatch completion paths in `scripts/okstra_ctl/dispatch_state.py`, granted artifacts in `scripts/okstra_ctl/dispatch_core.py`, and `scripts/okstra_ctl/report_assembly.py` `assemble_report`.

Before constructing the dispatch prompt, the lead MUST:

- Preserve the `**Report Language:**` value already materialized in the
  report-writer dispatch prompt. The dispatcher resolves project/global
  configuration and brief-language inference before the model receives the
  prompt; report assembly copies the immutable run-manifest value into the
  final record.

The convergence output provides four finding categories:

1. Full Consensus
2. Partial Consensus
3. Contested
4. Worker-Unique

All categories must appear in the final synthesis. Do not omit contested or worker-unique findings. If the brief does not define a stricter format, follow `instruction-set/final-report-template.md`. If `reference-expectations.md` defines explicit expected states for config files or deployment manifests, include a clear match/gap assessment.

If only one worker result is usable: reduced-confidence synthesis. If evidence is missing: say `I don't know`. If no meaningful worker differences: say so explicitly.

### Phase 6 sub-step: Plan-body verification (implementation-planning only, BLOCKING)

After the Report writer worker narrative is reviewed, **if** `task_type == "implementation-planning"` **and** `task-manifest.json` `convergence.planBodyVerification.enabled == true` (default), the lead MUST run the plan-body verification sequence on the consolidated plan body before declaring Phase 6 complete and entering Phase 7.

This is a Phase 6 sub-step — it does NOT introduce a new top-level lifecycle phase; the lead operating-phase model (Phase 1 Intake → Phase 7 Persist, labels in the "Quick Reference" table above as the single source of truth) is preserved. The round's outcome is read from the final report's `### 5.5.9 Plan Body Verification` section and `implementationPlanning.planBodyVerification` in its data.json — it is not a separate lifecycle phase identifier.

**REQUIRED RESOURCE:** Read the common procedure of [plan-body-verification](./plan-body-verification.md) for the round protocol, plan-item ID scheme (`P-Dir-1` for selected-direction; `P-Opt-*` for legacy candidate comparison; then `P-Step-*` / `P-Dep-*` / `P-Val-*` / `P-Rb-*` / `P-Req-*` / `P-Prep-*`), verdict semantics (`AGREE` / `DISAGREE(a-f)` / `SUPPLEMENT`), classification rules, gate-result resolution, and state-path authority. Read the state-file schema only when diagnosing state or projection validation. For `P-Dir-1`, compare `directionRealization` with `selectedDirectionRef` and its snapshot: verify the core mechanism, architecture boundaries, planning invariants, and any hidden direction change.

Read from the resolved runtime resource path, replacing `<plan-body-contract-path>`
with that resource's absolute path. This prints the complete common procedure and
its conditional reading table, stopping before reference examples:

```sh
awk '/^## Reference material$/ {exit} {print}' '<plan-body-contract-path>'
```

Follow the conditional reading table for later rounds and validation failures.
The command keeps the filename in the read trace used by
`validators/validate_session_conformance.py` `_ENTRY_GUARD_READS`.
The bounded output is checked by
`tests/contract/test_contract_examples_execute.py::test_plan_body_common_read_keeps_gate_rules`.

Distinct from Phase 5.5 finding convergence:

- Phase 5.5 reconciles worker **findings** (F-*) from independent analysis.
- This sub-step reconciles the **consolidated plan body** (P-*) authored by the Report writer worker.
- The two rounds use disjoint queues and separate state files — see [plan-body-verification](./plan-body-verification.md) "MUTUAL EXCLUSION (BLOCKING)".

Lead's responsibilities in this sub-step (in order):

For a new `implementation-planning` run, the fixed order is initial verification → one planner self-fix → targeted re-verification → lead decision or immediate user confirmation. The initial verification is round 1 and the targeted re-verification is round 2. A second automatic self-fix is a contract violation. When `okstra plan-items prepare` reports `"gating": false` (one-stage `no-design-inputs` plan), skip the self-fix loop and the sweep batch: extraction and round 1 still run, then go to the user gate. Two-or-more stages, a PREP item, or non-empty `designPreparation.items` keep `gating: true` and the full order.

1. Build the queue with `okstra plan-items prepare --narrative <report-writer-narrative.md> --run-manifest <run-manifest>`, place the output of `okstra plan-items prompt --run-manifest <run-manifest>` verbatim in every verifier prompt, then run `okstra plan-items validate-prepared --narrative <report-writer-narrative.md> --run-manifest <run-manifest>`. Python resolves the one convergence-owned state path from that run identity. The lead MUST NOT summarise, select, omit, reorder, or renumber the queue. Each prompt uses the compact subject plus the lossless payload, and asks every item:

   ```text
   What concrete false-positive input, failure ordering, or omitted dependency
   would make this plan item incorrect even if its happy path succeeds?
   ```

   An `AGREE` response records the considered counterexample and exclusion reason in its note; unverified external material is `verification-error`, not `DISAGREE`.
2. Dispatch a single plan-body reverify round to every analyser worker in the roster (`claude`, `codex`, and `antigravity` when opted in). `Report writer worker` is NOT a participant in this round.
3. Record each verifier Markdown result through `okstra plan-items apply-verdicts --state <plan-body-verification.json> --result <worker-id>=<result.md> --round <N>`. Python validates every submitted `P-*` identifier against the current convergence state and overwrites only that round's verdicts. Then resolve the gate result to one of `passed` / `passed-with-dissent` / `blocked-by-disagreement` / `aborted-non-result`.
4. After `okstra plan-verify` succeeds, run `okstra plan-items complete-round --state <plan-body-verification.json> --run-manifest <current-run-manifest.json> --round <N>`. Python reads the current worker assignments, atomically appends the convergence-owned history, and updates its nested final projection. This state is the only plan-verification input report assembly reads.
5. Record every *in-scope execution* `majority-disagree` decision through `okstra approval-decision`; record its plan and clarification links only on activities. Do not promote `observed` / `deferred` / `record` items, and do not append `clarificationItems[]` directly.
6. Run report assembly after the final plan-body state, approval ledger, design snapshot, activity ledger, and team state are complete. Assembly writes `implementationPlanning.planBodyVerification` and derived clarification rows while publishing `data.json` once.
7. Publish the report record `frontmatter.approved` field as `false`. There is no in-body `- [ ] Approved` marker line — approval lives only in the record (see [plan-body-verification](./plan-body-verification.md) §"Round protocol" step 9). The user may set it to `true` (via `--approve` or the in-session wizard) when the gate is `passed` / `passed-with-dissent`, or under `blocked-by-disagreement` once every remaining `Blocks=approval` row is user-proceeded (`accept-risk` / `select` / `answer`); `aborted-non-result` still withholds approval. **Enforced:** run-prep (`scripts/okstra_ctl/run.py` `_validate_approved_plan` / `_blocking_gate_survives_user_decision`) fail-closes an `approved: true` plan whose blocking gate carries no user disposition, and `validators/validate-run.py` `_validate_plan_body_gate_recompute` fails a gate value upgraded past what the recorded votes support. Manually flipping a blocked gate to passing is a contract violation.

If `convergence.planBodyVerification.enabled == false` (set by `--no-plan-verification` or by `okstra config set plan-verification off`), the entire sub-step is skipped and the top-of-report Approval marker is rendered unconditionally (legacy behaviour). This opt-out is intended for fast iteration only and is not recommended for handoff-ready plans.

## Phase 7: Artifact persistence and validator handoff

The detailed persistence sequence lives in [report-writer](./report-writer.md). Drive it through `okstra report-finalize`; do not patch any final-report field manually.

Order of operations:

1. Run `okstra report-finalize ...`. Contract v3 collects usage, assembles the role-owned inputs into `data.json` once, renders views, persists follow-ups, validates the run, records this run's conclusion into the task-group's `group-context.md` for the group's sibling tasks, and performs eligible teardown in order. When the task's pointer is terminal and the group has a task not yet started, the result's `nextInGroup` names it and `nextCommand` closes on starting it from its brief — the group's start order is the brief ordinal; the briefs' Related Task Graph edges are quoted as `waits for` and do not reorder it.
2. When `meta.reportLanguage` is not `en`, the same call dispatches the translator: its `translate` step (directly before `render-views`) materializes the translator prompt when this run has no undispatched reservation, runs the CLI-wrapper dispatch, and requires the `*.i18n.<lang>.json` sidecar. Do not dispatch the translator by hand or split the sequence around it; if the step fails, clear the cause and resume with the `--only translate --only render-views …` hint the result prints (**Enforced:** `okstra_ctl.report_translation_dispatch`).

Keep the assigned worker prompt history paths stable in `team-state`, `run-manifest`, and `task-manifest`. Do not rewrite prompt artifacts to `/tmp` or omit prompt metadata for attempted workers.

After persistence, reply briefly in the resolved Report Language. **Lead this reply with the run's task identity** — state `<task-group>/<task-id>` (or the full `taskKey`) first. Then: completion status, the task-qualified human report path, the report record path, validator result, any remaining blocker. **Close with the user's next action** — one command they can run now. A prohibition (`do not start implementation`) is not a next action. A status dump is not a close. This closeout is also in the launch prompt (`prompts/launch.template.md` "User closeout (BLOCKING)") so it is not lazy-read.

**Read the pointer from the `report-finalize` result — do not re-derive it.** Its top-level `nextRecommendedPhase` object (`phase`, `status`, `rationale`) is what the rows below branch on, and the same values are repeated on stderr as `next phase status:` / `next phase:` / `next phase rationale:`. You author your task type's routing field; Phase 7 projects the pointer from it, and for `implementation-planning` there is no routing field to re-derive from at all. A `nextRecommendedPhaseError` in that result means the pointer could not be read — say so in one line and close on the `validate-run` row. 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.

Pick the next action from this table; the first matching row wins:

- Inside an already authorized continuous task, report the boundary and continue the confirmed stage queue or authorized ready phase. Do not ask the user to invoke the same workflow again solely because one run ended. A recorded wizard receipt remains limited to its original scope; cite broader authorization separately and preserve actual approval and host-permission boundaries.
- Phase 7 failure with `recovery.mode: same-run` → continue correcting the owner inputs and execute `recovery.resumeCommand` in this run. Preserve user approvals and model choices. Read the report-writer recovery procedure before correcting its narrative. Do not close by asking the user to restart the phase for repairable artifact errors. If the only failure is `validate-run` and a blocked pointer targets an earlier phase, `recovery.mode: phase-reentry` preserves that target in `nextCommand`. Other validator findings are advisories, printed as `validate-run: advisory — <finding>`; follow the matching pointer row when the run passed.

- Open `blocks: approval` rows, or a `blocked-by-disagreement` gate whose approval rows carry no progress disposition (`okstra_ctl.run._blocking_gate_survives_user_decision`) → `/okstra-user-response` (name the `C-NNN` ids). Do not start implementation until those answers exist. An `accept-risk` / `select` / `answer` already recorded is not an open blocker. A report published with those rows open is not a failed run — the answers are the missing input, and a planning re-run without them reproduces the same block.

After `implementation-planning`, once finalization succeeds, stop on the approval row above. No open approval blocker → `/okstra-run` → `implementation` (asks `approve_plan_confirm`) or `--approve`. Do not propose another planning run. Do not say `/okstra-inspect`. A blocking `validate-run` failure follows the recovery row first; a `validate-run` advisory is named in one line and nothing else — it did not fail the run.

For every other task type:

- Pointer `status: ready` → `/okstra-run` for that `phase`. Quote the pointer's `rationale` — that sentence is this run's report saying why that phase comes next, and it is the analysis the user asked for.
- Pointer `status: terminal` → the lifecycle ends here. Say the task is finished and quote the pointer's `rationale`. Do not propose a run and do not send the user to `/okstra-inspect`: the decision is already made, so there is nothing to inspect. If this run registered follow-up tasks, name them and the command that starts one.
- Pointer `status: terminal` and the result carries `nextInGroup` → the task is finished and the task-group has a task not yet started: say this task is finished, then close on `/okstra-run` for `nextInGroup.briefId` from its `brief` path — the group's start order is the brief ordinal, and `nextCommand.note` already names the task and its brief.
- Pointer `status: blocked` → the pointer's `rationale` names what is in the way and what to run. Quote it and issue that command — `/okstra-user-response` for the `C-NNN` ids it lists, or `/okstra-run` for the phase it names.
- Pointer `status: pending` with a `rationale` → the user's own input is what comes next. Quote the `rationale` and issue the command it names. Do not re-run the phase that just completed, and do not send the user to `/okstra-inspect`: a finished phase has nothing to inspect, and re-running it discards the result the user is being asked to act on.
- Otherwise → `/okstra-inspect status` for this task.

When the host native picker is available and two of those rows could apply, ask with that picker (recommended first). Do not end the turn after the status dump.

**Cite run-artifact paths, do not assemble them.** The `report-finalize` result's `reportPaths` carries this run's `humanReport`, `reportRecord`, `teamState`, and `renderFullCopy` command, each already rooted at the project (`.okstra/tasks/<task-group>/<task-id>/runs/...`). Every other run-artifact path the reply cites — the resume command among them — comes from the launch prompt's `## Manifests` / `## Run Paths` lists, which are rooted the same way. A path you compose from a `runs/<task-type>/...` pattern instead is identical across every task of that task-type, so it names no task and does not resolve from the project root either.

Write file references as Markdown links with short labels in the Report Language, following the launch prompt's file-link presentation guidance. `reportPaths.markdown` supplies verified destinations for the report, report record, and team state; preserve those destinations and shorten only their display labels. Put each link on its own line without repeating the path in prose or inserting a line break inside the destination. Apply the same format to worker results, error logs, and briefs, using the project-rooted paths from `## Manifests` / `## Run Paths`. If the terminal expands links into long paths, offer the host-appropriate copyable file-opening command described in the launch prompt. Markdown alone does not guarantee clickable links in every terminal.

**Enforced:** `scripts/okstra_ctl/report_finalize.py` `_closeout_report_paths` builds the task-qualified paths and `closeout_command` builds the close, so neither is re-derived; `validators/validate_session_conformance.py` `_check_progress_checkpoints` requires the `phase-7-persist` checkpoint this phase opens with.

The run-level error log lives at `<runDir>/logs/errors-<task-type>-<seq>.jsonl`. It is informational. Its presence or absence does not affect the final verdict. Do not block report writing on it.

## Run-scoped worker-resource lifecycle

- At run start, call the selected adapter's setup required to distinguish lead-owned resources from worker-owned resources.
- Before every new worker batch, and between worker rounds within a phase, close **every** resource the prior round's completed workers still hold — display surfaces, roster entries, and live execution handles alike — before the next dispatch; never the lead and never an in-flight worker. Call `record_lead_event` for the batch-cleanup checkpoint. Which resources exist and how each one is released is the selected adapter's mapping.
- Before the lead asks the user for any approval, clarification, or decision through the runtime's prompt primitive after workers have been dispatched, first close every resource the completed workers still hold, exactly as at a round boundary — so no user gate is shown while a finished worker's display surface is still open. Never the lead and never an in-flight worker. Record the gate-cleanup checkpoint.
- Closing a completed worker's resources is one paired operation: releasing its display surface and stopping its live execution handle together. Stopping the execution handle alone idles the roster entry but leaves its display surface open, so a handle-stop by itself is never cleanup — every cleanup point (round boundary, user gate, run end) does both.
- After Phase 7 persistence and `collect_usage`, enumerate residual adapter-owned resources. If none remain, skip the question.
- If resources remain, call `prompt_user` once with a binary keep-or-clean choice. The answer controls the entire residual set; do not ask a second backend-specific cleanup question.
- On keep, preserve all resources and provide the selected adapter's manual-cleanup instruction.
- On clean, emit the teardown checkpoint and call `shutdown_workers` through the selected adapter. Never execute another adapter's cleanup primitive.
## Common Mistakes

| Mistake | Fix |
|---------|-----|
| Calling a host primitive from an unselected adapter | Read the selected adapter path from the launch prompt and execute only its semantic-operation mappings |
| Substituting lead reasoning for a worker result | The lead synthesizes only — call `dispatch_worker` for the selected assignment |
| Skipping a worker silently | Always record terminal status with reason |
| Writing verdict before all workers report | Wait for all results or explicit terminal statuses |
| Ignoring task bundle model assignments | Task bundle overrides are canonical |
| Inserting per-worker emphasis sentences ("you focus on X") into dispatch prompts | Send byte-identical dispatch prompts per [team-contract](./team-contract.md) "Dispatch-prompt invariant" — specialization lives in Section 6 of the worker output, not the prompt body |
| Omitting contested or worker-unique findings | All categories must appear in the report |
| Running full re-analysis when lightweight suffices | Default lightweight; full only when manifest opts in |
| Using `/tmp/*prompt*.txt` for worker prompt persistence | Persist the exact worker prompt to the assigned run-level `prompts/` path |
| Lead writes `final-report-<suffix>.md` itself when `Report writer worker` is in the roster | Dispatch the Report writer worker per [report-writer](./report-writer.md) "Phase 6 dispatch template" |
| Skipping Report writer worker dispatch citing "session resume constraint" or "team is gone" | No such constraint exists — see [report-writer](./report-writer.md) "Resume-safe dispatch" |
| Selecting a generic worker assignment for Report writer worker | Use the rostered `report-writer-worker` assignment; the selected adapter owns its native field mapping |
| Including `final-report-template.md` in analysis worker `[Required reading]` | Template belongs only in the report-writer prompt — see [team-contract](./team-contract.md) audience-scoped enumeration |
| Injecting `[Required reading]` into lightweight reverify prompts | Lightweight reverify forbids re-reading source materials — see [convergence](./convergence.md) "Reverify prompt: required-reading suppression" |
| Letting `convergence.maxRounds` default to 2 for `requirements-discovery` | Resolve effective default to `1` for discovery and put it in the grouped input |
| Issuing serial Read calls in Phase 1 | The intake files are independent — issue all Read calls in a single message (parallel) |
| Flagging an adapter-shortened dispatch prompt as "incomplete" because it omits host-loaded material | The Worker Preamble pointer and core inputs remain mandatory; the selected adapter may omit duplicate host-loaded definitions |
| Waiting silently after `dispatch_worker` returns without a completed worker artifact | A dispatch acknowledgement is not completion — call `await_workers` and enforce the selected adapter's liveness policy |
| Re-sending a finding absent from the persisted round plan | Dispatch exactly the engine-returned `findingIds`; see [convergence](./convergence.md) "Re-verification Dispatch" |
| Aggregating a `timeout`/`error` reverify dispatch as `DISAGREE` | Put the terminal outcome in round results; `apply-round` records `verification-error`. See [convergence](./convergence.md) "Worker failure handling in reverify" |
| Bypassing `report-finalize` and running its Phase 7 steps manually | Run `okstra report-finalize ...`; it owns token substitution and the remaining persistence order. |

## Recoverable completion discrepancies

Treat `plannedPaths` as an expected change inventory, not an exhaustive authorization list. `ExecutionMutationAudit.compare` enforces write authority and emits plan discrepancies as warnings; `dispatch_state.missing_completion_paths` checks required result availability and usability. Collect usable results with warnings and pass the observed changes and unresolved evidence to independent verification before declaring task completion.

Preserve original results and attempt identities when correcting declaration formatting, missing rationale, or required artifacts. Continue the smallest affected repair within the approved objectives. Do not restart implementation or request renewed permission solely for a plan-path mismatch. If a required artifact is unusable, repair that artifact and keep only its dependent steps waiting. If the same repair produces no new evidence, report the unresolved requirement and resume point instead of repeating the whole implementation.

Ask for user judgment only when the proposed work changes approved objectives or acceptance conditions, crosses an explicit authority boundary, or adds an unapproved external action. A warning does not waive read-only roles, protected paths, independent verification, or final acceptance checks. Never convert an old closed failed attempt into success by overwriting its audit; use a recorded recovery with independent evidence.

For a closed attempt whose only failure was a declaration discrepancy, preserve that attempt and its original result. Use the existing `agent-prompt materialize` and dispatch workflow to create a bounded evidence-recovery invocation in the same run, with a distinct result path and explicit references to the original invocation, result, and audit. Ask it to reconcile the declaration and required artifacts against current evidence, not repeat completed implementation. Then dispatch the selected independent verifiers against the current source and artifacts. Adopt the new verified result through normal result collection; retain the original failure as history. Do not use this route to bypass a real authority violation or unresolved code defect.
