# Storage model & on-disk contracts

> Detailed storage and contract section from [`docs/architecture.md`](../architecture.md). This section was split out after the main document exceeded 1,000 lines.

## Storage model

Files created by `okstra` are stored in the following three areas according to their purpose.

### 1. Stable task root

This is the task's primary directory.
The task manifest, task index, instruction set, runs, and history are collected under this root.

- `<target-project>/.okstra/tasks/<task-group>/<task-id>/`
  - `task-manifest.json`
  - `task-index.md`
  - `instruction-set/`
    - `analysis-profile.md`, `analysis-packet.md`, `analysis-material.md`, `reference-expectations.md`, `task-brief.md`
    - `verification-target.md` for final verification and optional `directive.txt`
    - `host-orchestration-rules.md`, the staged copy of the host orchestration rules for this task type, when the run has one
    - `clarification-response.md`, the user's carried-in clarification answers, when the run has any
    - `selected-direction.json`, the validated implementation direction snapshot for a new `implementation-planning` run
    - `final-report-schema.json`, `final-report-template.md`
    - canonical `lead-execution-prompt.md` plus the `claude-execution-prompt.md` compatibility alias
  - `runs/<task-type>/`
    - `manifests/run-manifest-<task-type>-<seq>.json`
    - `state/team-state-<task-type>-<seq>.json`
    - `prompts/lead-execution-prompt-<task-type>-<seq>.md` and selected `<worker>-worker-prompt-<task-type>-<seq>.md` files, each with adjacent `.meta.json` invocation metadata
    - `prompts/duty-contracts-<task-type>-<seq>/`, the immutable run-scoped duty catalog snapshot
    - `reports/final-report-<task-type>-<seq>.{data.json,md,html}`
    - implementation-planning-only `design-prep-requests/` and `design-prep-inputs/`
    - `status/final-<task-type>-<seq>.status`
    - Claude compatibility launcher sessions under `sessions/claude-resume-<task-type>-<seq>.sh`
    - optional lead-written `logs/errors-<task-type>-<seq>.jsonl`
    - `worker-results/`
  - `history/timeline.json` and optional append-only `history/fix-cycles.jsonl`
  - optional append-only `recap/recap-log.jsonl`
  - optional inert agent notes under `notes/<slug>-<YYYY-MM-DD>.md`; a run reads them only when explicitly carried through `--clarification-response`

Actual directory segments may be normalized into slugs to create safe paths.

v2 run artifacts persist `participantRef`, `roleExecutionRef`, `invocationRef`, `attempt`, and `executionLabel` as stored fields. Readers must not reparse those strings from `workerId`, provider, or model labels. v1 bytes stay unread-write. A mixed or partial v2 payload is rejected.

Shared Git common directories and other-stage refs are outside the mutation-audit enforcement surface. Dispatch records observed projection only; it does not claim to police every ref that a shared object store can see.
However, the logical task key always retains the original input values in the form `project-id:task-group:task-id`.

### 2. Per-run execution artifacts

Artifacts from each execution accumulate beneath the current run's resolved `run_dir`.
`runs/<task-type>/` is the run directory for non-stage runs. As exceptions, `implementation` uses `runs/implementation/stage-<N>/`, and single-stage `final-verification` uses `runs/final-verification/stage-<N>/` as its `run_dir`. Whole-task `final-verification` retains the non-stage form `runs/final-verification/`.

```text
<target-project>/.okstra/tasks/<task-group>/<task-id>/runs/<task-type>/
```

The representative files below are all relative to the resolved run directory (`run_dir`). Therefore, report, log, and session paths for stage-isolated runs include the `stage-<N>/` segment. The carry sidecar is the exception — it stays flat under the task-type run dir (see the `carry/stage-<N>.json` entry below).

- `manifests/run-manifest-<task-type>-<seq>.json`
- `state/team-state-<task-type>-<seq>.json`
- `prompts/lead-execution-prompt-<task-type>-<seq>.md` *(canonical; the Claude-named snapshot remains a compatibility alias)*
- `prompts/<worker>-worker-prompt-<task-type>-<seq>.md`
- `prompts/<prompt>.md.meta.json` *(invocation identity, assignment, duty identity, source paths, and the five catalog/assignment/duty/instruction/prompt digests)*
- `prompts/duty-contracts-<task-type>-<seq>/` *(run-scoped snapshot; dispatch verification rejects drift from its catalog digest)*

After the host-native lead takes over, the lead and its assigned workers add the following result files to the current run.
- `sessions/claude-resume-<task-type>-<seq>.sh`
- `reports/final-report-<task-type>-<seq>.data.json` *(schema v2 source of truth for both audiences)*
- `reports/final-report-<task-type>-<seq>.md`
- `reports/final-report-<task-type>-<seq>.html`     *(Phase 7 deterministic post-processing: task-specific, self-contained HTML for human reviewers, with inline CSS/JS)*
- `design-prep-requests/design-prep-request-<seq>-PREP-<NNN>.md` *(implementation-planning only: deterministic AI-prepared request for a `provisional` or `blocked` design item)*
- `design-prep-inputs/design-prep-input-<seq>-PREP-<NNN>-r<revision>-<uuid>.md` *(implementation-planning only: append-only confirmed user/wizard response)*
- `user-responses/user-response-<task-type>-<seq>.md` *(A sidecar downloaded under the same name by the HTML `Export user response` button; storing it here lets `--resume-clarification` automatically attach it to `clarification-response.md` in the instruction set — `clarification_items.clarification_response_with_sidecars`)*
- `worker-results/<worker>-audit-<task-type>-<seq>.md` *(Per-worker Reading Confirmation sidecar; for auditing rather than report content)*
- `status/final-<task-type>-<seq>.status`
- `runs/implementation/carry/stage-<N>.json` *(implementation only: execution evidence sidecar. Stage-SHARED like `consumers.jsonl` — it lives flat under the task-type run dir, NOT under `stage-<N>/`, because the next stage's carry-in and `backfill_done_from_carry` glob it without knowing the producing run's layout)*
- `consumers.jsonl` *(implementation-planning only: backlinks to the impl-run that consumed each stage in this plan; append-only)*

#### Direction-selection handoff authorities

| Artifact | Content authority | Digest contract |
|---|---|---|
| Option-selection data report | The immutable `data.json` records the validated candidates. | It does not store the handoff digest. |
| User-response sidecar | In comparison mode, the sidecar records the confirmed `IO-NNN` choice without changing the report. | `source-data-sha256` records the digest of the exact `data.json` bytes. Planning prepare recomputes SHA-256 over those bytes and rejects a mismatch. |
| Selected-direction snapshot | Planning prepare writes the normalized choice to `instruction-set/selected-direction.json`; the planning report cites it through `selectedDirectionRef`. | `sourceDataSha256` records the digest that planning prepare verified. |

Preselected-validation mode records its upstream confirmation in the option-selection report and needs no user-selection sidecar. Planning prepare still writes the selected-direction snapshot with the verified source-data digest.

Design-preparation storage has three separate authorities:

| Artifact | Owner | Mutation contract |
|---|---|---|
| `reports/final-report-implementation-planning-<seq>.data.json` | report writer / approved planning snapshot | Immutable after approval. Phase 7 may populate report-owned cells before approval, but a later CLI or wizard response never edits this snapshot. |
| `design-prep-requests/design-prep-request-<seq>-PREP-<NNN>.md` | Okstra | Deterministic from the snapshot, report language, and request-template version. Re-materialization is idempotent; different existing content is never overwritten. |
| `design-prep-inputs/design-prep-input-<seq>-PREP-<NNN>-r<revision>-<uuid>.md` | user or wizard | Append-only and written only after semantic confirmation. Existing revisions are never edited or reserved before confirmation. |

The resolver selects the highest unique revision whose assessment fingerprint still matches the approved snapshot. Duplicate revision numbers and stale fingerprints are skipped with warnings rather than granted authority. A markerless legacy plan has no sidecars to migrate: it proceeds with the `legacy-unassessed` warning. This compatibility path does not rewrite its report.
The final result files are not created by saving worker stdout. The report writer writes its narrative Markdown, and Phase 7 assembles `final-report-<task-type>-<seq>.data.json` from all role-owned inputs before deriving the human HTML sibling. The full reading copy Markdown is rendered on demand with `okstra render-final-report`. New bundles use `schemas/final-report-v3.0.schema.json`; historical v2 data remains readable through `schemas/final-report-v2.0.schema.json`. Both use `templates/reports/final-report-v2.template.md` for the reading copy. Rendering never modifies source data or a pre-existing Markdown input.
For the standalone Claude launcher, `sessions/claude-resume-<task-type>-<seq>.sh` remains an interruption-recovery compatibility helper. In-host Codex runs use host task continuity plus the generic `leadSession` manifest identity.

The resolved run directory collects execution history. It divides its contents into type-specific subdirectories such as `manifests/`, `state/`, `prompts/`, `reports/`, `status/`, `sessions/`, and `worker-results/`, then distinguishes each run-level artifact and result file with a `-<task-type>-<seq>` suffix (a three-digit, zero-padded per-category counter, such as `001` or `002`).
Worker prompt history is retained not under `/tmp`, but always as a canonical artifact under `prompts/` for the current run.

`state/team-state-<task-type>-<seq>.json` stores invocation-aware dispatch
records. Code-owned worker launches record the invocation fields directly in
`workerDispatches[]`, including the `sessionId` the dispatch minted for that
attempt. That id is the only handle on a worker that runs as its own CLI
process, because such a session tags its transcript with neither an agent nor a
team name; it is empty for providers whose CLI takes no session-id argument. A
row is keyed by its dispatch ID rather than appended blindly: a row repeating a
`dispatchId` already present replaces that row in place, so one dispatch ID is
recorded at most once and a re-sent dispatch cannot make the run read as a
duplicate. Rows carrying no dispatch ID have no key to collapse on and still
stack. Nothing prunes the array and no row is ever dropped, so a replaced row
takes its `paneId` out of the ledger with it and the pane-reclaim gate no longer
offers that pane. Host-native calls use `agentDispatches[]`, and
`agentResultLinks[]` associates an accepted result path with exactly one
dispatch ID. The inverse is also exclusive: one dispatch ID cannot authorize
multiple accepted paths. State mutation uses a run-state lock and unique
temporary files so parallel dispatch and result-link writers do not lose each
other's updates. `host-native-spec-link-gate` never means that the host attested the
delivered prompt bytes; `promptDeliveryVerified` remains false.

The complete run manifest is published before any run-backed prompt. Its
`agentContract`, `invocationAssignments`, run-scoped duty root, catalog digest,
and invocation reservation root are immutable for the lifetime of the run.
Every invocation metadata file refers back to that manifest and to one
`assignmentRef`. A reservation binds `invocationId`, `workerId`, assignment,
audience, dispatch kind, prompt path, and adjacent metadata path before the
prompt can be reused. This run-scoped state belongs under `runs/.../prompts/`,
not the task-wide `instruction-set/`, because consecutive runs and parallel
implementation stages must not share mutable invocation identity.

The manifest's `authorizedPaths` allowlists instruction, prompt, and result
roots. Materialization resolves each requested path to its real path, rejects a
path outside the corresponding root, rejects a symbolic-link escape, and
rejects an undeclared result path before writing. Persisted instruction
provenance is a non-empty list of `{kind: project|runtime, path: <relative POSIX
path>}` objects. The path is normalized POSIX text relative to its named
authority; installed runtime absolute paths are not durable metadata.

Prompt publication is recoverable rather than pretending that two independent
files can be replaced atomically. A create-only prompt is written first and its
adjacent metadata is written last as the completion marker. Under the
per-invocation publication lock, a retry recovers a byte-identical orphan
prompt, but rejects different existing bytes. A reservation lock prevents two
concurrent writers from mixing prompt and metadata identities.

LLM calls outside a task run use
`<project>/.okstra/agent-invocations/<purpose>/`. Identity is scoped by the
`(purpose, invocationId)` pair, and both values must be canonical slugs. For an
invocation ID `<invocation-id>`, the exact immutable paths are
`<invocation-id>.instructions.md`, `<invocation-id>.prompt.md`, adjacent
`<invocation-id>.prompt.md.meta.json`, `<invocation-id>.result.json`, and
`<invocation-id>.prompt.md.completion.json`, plus a private `.tmp/` capture
area. The result envelope binds purpose, invocation ID, prompt metadata path,
and `returnedBody`; the completion marker is published last and binds the
canonical result envelope digest. The same raw returned body may be used by two
independent invocations because identity comes from the envelope, while a
cross-invocation envelope link is rejected. Code review uses purpose
`code-review`; schedule narrative verification uses `schedule-verification`.
Consumers parse only the `returnedBody` emitted by
`agent-prompt verify-completion`. A branch review's final report may use the
documented `.project-docs/code-reviews/<branch>/` exception, but its invocation
specification, result envelope, and completion record remain under `.okstra/`.

The persisted prompt's `**Worker Preamble Path:**` records the selected functional audience contract: analysis uses `templates/worker-prompt-preamble.md`, implementation executor/verifier uses `templates/implementation-worker-preamble.md`, and report writing uses `templates/report-writer-prompt-preamble.md`. Every initial prompt also persists `**Worker Error Contract Path:** templates/worker-error-contract.md`. Active-run and run-context runtime resources expose the same audience map plus the shared error-contract path, so dispatch replay and context-cost accounting do not infer an audience from a provider/model name.
Unlike before, `analysis-profile.md`, `analysis-material.md`, `reference-expectations.md`, `task-brief.md`, skill copies, and `final-report-template.md` are not duplicated for every run.
These materials retain canonical copies in `instruction-set/` under the stable task root.
For `final-verification`, `verification-target.md` is also stable-task instruction-set state. Prepare owns the snapshot and stores its project-relative path plus SHA-256 digest in task/run manifests and active-run context. Initial analysis prompts carry the compact worktree/scope/base/head/path/digest identity and read this sidecar on demand instead of copying its diff stat.
Each wrapper `<prompt>.log` beside a persisted `<prompt>.md` is a growing execution transcript, not another prompt snapshot. `okstra log-report` pairs the siblings and reports prompt/transcript bytes separately while keeping the legacy log-oriented `sizeBytes` and `totalBytes` fields.
When the same non-stage task type runs again, it reuses the same `runs/<task-type>/` run directory. Reruns of `implementation` and single-stage `final-verification` reuse their resolved `runs/<task-type>/stage-<N>/` run directories. In both cases, existing artifacts are not overwritten because run-level filenames are separated by `-<task-type>-<seq>` suffixes within type-specific subdirectories. Because `<seq>` is scanned independently for each category directory (`manifests/`, `prompts/`, `reports/`, `status/`, `state/`, `sessions/`, `worker-results/`) beneath the resolved run directory, values may differ by category even within the same run.
If flat legacy artifacts remain at the top level of a task-type run directory, the next execution automatically moves them into the corresponding type-specific subdirectories.

### 3. Project-level discovery and installed skill assets
Shared project discovery pointers are created at the following paths.

```text
<target-project>/.okstra/discovery/latest-task.json
<target-project>/.okstra/discovery/task-catalog.json
```

Division of responsibilities:

- `.okstra/discovery/latest-task.json`: a current-task convenience pointer to the most recently prepared okstra task bundle in the current project
- `.okstra/discovery/task-catalog.json`: the canonical project-level catalog that maintains the list of okstra task bundles prepared in the current project by `taskKey`, `taskGroup`, and `taskId`
- `instruction-set/reference-expectations.md`: a task-level canonical artifact that identifies the config files, deployment manifests, and expected values the current task should reference
- `~/.claude/skills/okstra-*/...`, `~/.agents/skills/okstra-*/...`, `~/.claude/agents/...`: skill/agent assets seeded into the user's home directory by `okstra install` (project-local seeding no longer occurs; refresh them with `okstra install --refresh`)

The following former files are no longer generated by okstra.

- `CLAUDE.md`
- `.project-docs/ai/claude-project-guide.md`
- `.project-docs/ai/claude-skill-index.md`
- `.project-docs/ai/okstra/okstra-guide.md`
- `.project-docs/ai/okstra/worker-catalog.md`

### 4. Manager-owned cross-project state

`okstra manager` does not replace the project-local `.okstra` canonical state. It stores global manager state that groups multiple project-local tasks in the user's home directory.

```text
~/.okstra/managers/<manager-id>/
├── manager.json
├── projects.json
└── task-groups/<safe-task-group>/<safe-task-id>/
    ├── manifest.json
    ├── children.json
    ├── directives.jsonl
    ├── snapshots.json
    ├── events.jsonl
    └── child-context/<safe-project-id>-<safe-child-task-id>.md
```

Storage authority:

- `manager.json`: manager identity and schema version.
- `projects.json`: the projectId / projectRoot / role / tags registered with the manager. `projectRoot` must be an existing directory, and setup-equivalent registration is performed only when project-local `.okstra/project.json` is absent.
- `manifest.json`, `children.json`, `directives.jsonl`: manager-owned plan, child assignment, and shared/project directives.
- `snapshots.json`: a read-side snapshot that `task sync` imports from the child project's `.okstra`. It is not the source of truth for project state.
- `events.jsonl`: manager events such as `task-created` and `child-launch-prepared`.
- `child-context/*.md`: child lead context prepared by `task run`. Sibling project reports/snapshots are passed only as read-side source material.

Path segments are normalized into slugs. If a slug would be empty, as can happen with non-ASCII values, a `u-<sha1-prefix>` fallback segment is used. However, `manifest.json` and the child `taskKey` preserve the original input values (`project-id:task-group:task-id`).

## Task manifest contract

`task-manifest.json` is the canonical metadata file the active Okstra lead uses to understand task continuity.
As a general rule, this manifest records the absolute `projectRoot` path only once and expresses the remaining generated paths as project-relative fields whenever possible.

Examples of key fields:

- `projectId`
- `taskGroup`
- `taskId`
- `taskKey`
- `projectRoot`
- `taskType`
- `workCategory`
- `taskBriefPath`
- `relatedTasks`
- `currentStatus`
- `taskRootPath`
- `instructionSetPath`
- `referenceExpectationsPath`
- `runsPath`
- `historyTimelinePath`
- `latestRunPath`
- `latestRunStatus`
- `latestRunPromptsPath`
- `latestReportRecordPath`
- `latestResumeCommandPath`
- `workflow.currentPhase`
- `workflow.currentPhaseState`
- `workflow.phaseStates`
- `workflow.lastCompletedPhase`
- `workflow.nextRecommendedPhase` *(object `{phase, status, rationale}`; `status` is `ready` / `pending` / `blocked` / `terminal`. The lead writes `phase` only under `ready` — an authoring rule, not a constraint the struct enforces: `prepare` lowers a `ready` pointer to `pending` and keeps its `phase`, so a stored non-`ready` pointer that still names a phase is a normal manifest state. Read `status` to decide whether anything can be launched.)*
- `workflow.nextRecommendedPhaseCorrection` *(present only when Phase 7 validation replaced the authored pointer — `{authored, applied}`)*
- `workflow.awaitingApproval`
- `workflow.lastSafeCheckpoint`
- `phaseOutcome` *(artifact-derived semantic phase outcome; for example, when the implementation carry proves that all stages passed, the implementation phase outcome may be recorded as completed even if run contract validation failures remain)*
- `inputs`
- `artifacts`
- `resultContract`
- `leadSession`
- `claudeSession` *(Claude-host compatibility only)*
- `fixCycles` *(derived summary — `{count, openCycleId, latest:{cycle, symptom, targetReport, closedAt}}`; recalculated on every prepare)*

This manifest serves the following purposes.

- Let the active lead understand task continuity before reading the brief
- Establish which files should be read first
- Establish how config files and deployment manifests should be interpreted against expected values
- Establish the task key under which results accumulate
- Make it quick to locate related tasks and the latest run

## Task index contract

`task-index.md` is a summary document for quick human reading.

Main contents:
- task key
- current task type
- work category
- current task status
- latest run status
- current phase
- current phase state
- next recommended phase
- reference expectations
- latest run
- latest report
- resume command

This document is only a quick summary and is not the source of truth.
Always use `task-manifest.json` when checking canonical metadata.

The rendered `task-index.md` is an English-literal artifact. The headings, field labels, and `## Notes` footnote of the template (`templates/project-docs/task-index.template.md`) ship as English literals regardless of the `reportLanguage` setting, matching the okstra-schedule-gen schedule contract. `tests/contract/test_docs_runtime_contract.py` enforces this contract.

## Run manifest contract

Each execution records the current run contract in the resolved `<run-dir>/manifests/run-manifest-<task-type>-<seq>.json`. The non-stage or stage-qualified rule above determines `<run-dir>`.

`manifests/run-context-<task-type>-<seq>.json`, starting with schemaVersion `2.0`, stores `identity` + `pathHints` rather than directly storing every legacy path key. The host-side reader hydrates `pathHints` to reconstruct the former flat keys (`RUN_MANIFEST_RELATIVE_PATH`, `TEAM_STATE_PATH`, and so on) in memory.

`state/active-run-context-<task-type>-<seq>.json` is also the compact intake for lead Phase 1 starting with schemaVersion `2.0`. It stores only `identity` + `pathHints` and worker identities instead of repeated path strings, and the deterministic dispatcher hydrates it into the legacy active context shape when reading it.

The path fields in `manifests/run-manifest-<task-type>-<seq>.json` are still stored as paths relative to the target project root for validator, team dispatch, and inspect compatibility.
An in-host prepare records the run as `prepared` and the task as `ready-for-lead`; the current native host session then owns execution. The standalone Claude launcher records `in-progress` after preparing its compatibility resume script.

Main contents:

- task key
- task type
- work category
- run datetime segment
- task brief relative path
- analysis target
- related tasks
- selected workers
- worker model assignments
- lead session id and accounting mode
- Claude session id only on the Claude compatibility path
- resume command relative path
- expected report relative path
- expected status relative path
- prompt snapshot relative path
- final-verification target snapshot relative path and digest (empty for other task types)
- `worker prompt directory relative path`
- `worker prompt relative path by worker id`
- current run status
- workflow snapshot
- team contract
- `fixCycleId` *(only when attached to a fix cycle; field omitted otherwise)*

## Timeline contract

`history/timeline.json` accumulates the history of runs belonging to a task.

The history generally includes the following.

- run timestamp
- run directory relative path
- run manifest relative path
- run time segment
- task type
- work category
- status
- worker prompt directory relative path
- report relative path
- resume command relative path
- related tasks
- workflow snapshot
- `fixCycleId` *(only when attached to a fix cycle; field omitted otherwise)*
Reruns of the same task type reuse the same resolved run directory family. Non-stage runs use `runs/<task-type>/`, while `implementation` and single-stage `final-verification` runs use `runs/<task-type>/stage-<N>/`. Sequence-specific artifacts live beneath that resolved run directory, and each execution accumulates as a separate history entry because `run-manifest-<task-type>-<seq>.json` and related artifact paths are separated by per-category sequence suffixes (`<task-type>-<seq>`). The cross-category identifier is the manifest's `runDateTimeSegment` ISO timestamp field.

## Lead operating contract

After `okstra` prepares a run, the host-native Okstra lead follows this default sequence when reading the current task.

1. If task browsing or task-id disambiguation is required, first read `.okstra/discovery/task-catalog.json`.
2. If the current task key or task path is not specified, read `.okstra/discovery/latest-task.json` as the current-task pointer.
3. Read `task-manifest.json`.
4. If the current `state/active-run-context-<task-type>-<seq>.json` exists, read it as the primary input for lead Phase 1. This file is a compact intake that reconstructs run artifact paths from `identity` + `pathHints` clues. If it does not exist, fall back to the current `manifests/run-manifest-<task-type>-<seq>.json` and `team-state`.
5. Read `instruction-set/analysis-profile.md` and `instruction-set/analysis-packet.md`.
   For final-verification, use the compact target identity from active context and open `instruction-set/verification-target.md` only when its full mapping or diff stat is needed.
6. Read `task-index.md` only when a quick summary is needed.
7. Lazily read `analysis-material.md`, `reference-expectations.md`, `task-brief.md`, and `final-report-template.md` when the packet is insufficient or when source citations/report writing require them.
8. Consult `history/timeline.json` and previous run results if necessary.
9. As `Okstra lead`, organize roles from `workerAssignments[]`. The host provider stays native; Claude, Codex, Antigravity, Grok, and Kimi assignments use their persisted `native-session` or `cli-wrapper` runner.
10. Save each selected worker prompt under the current run's `prompts/` directory at the assigned worker prompt history path before dispatching the worker.
    Every selected initial analyser receives the same full-core semantic body for its task, including final-verification. Dispatch and Phase 7 validate the same normalized equality policy; implementation executor, report-writer, and `-reverify-r<N>-` prompts are separate audiences.
11. In convergence Round 0, the lead groups findings by semantic meaning and ticket set, then writes `state/convergence-groups-<task-type>-<seq>.json`. The deterministic engine seeds the working queue and generates each roster-aware reverify plan; lightweight reverify reads only that persisted batch and its embedded evidence.
12. Collect a result or terminal status for each required worker.
13. Write the final structured report to `reports/final-report-<task-type>-<seq>.data.json` using the versioned schema and exactly one task-type deliverable.
14. Run Phase 7 so the task-specific human HTML is derived from the report record, then update `status/final-<task-type>-<seq>.status`, `manifests/run-manifest-<task-type>-<seq>.json`, `task-manifest.json`, and `task-index.md` to match the current state. The full reading copy is rendered on demand.

Convergence keeps its decision trail under the run's `state/` directory:

- `state/convergence-groups-<task-type>-<seq>.json` — lead-authored semantic groups and resolved analyser roster.
- `state/convergence-work-<task-type>-<seq>.json` — engine-owned queue, finding state, and round history.
- `state/convergence-round-<N>-plan-<task-type>-<seq>.json` — immutable roster-aware dispatch plan for one round.
- `state/convergence-round-<N>-results-<task-type>-<seq>.json` — adapter outcomes and parsed votes supplied to the reducer.
- `state/convergence-<task-type>-<seq>.json` — validated terminal schema v1.2 state consumed by reporting and Phase 7.
- `state/migrations/` — byte-for-byte archives of invalid or partial legacy state replaced during an explicit Round 0 restart.

A valid terminal legacy final is reused unchanged, and a matching valid working state resumes. Invalid new-engine working state fails closed unless seeding receives `--restart-from-round0`; an existing final can be replaced only when its migration archive still matches its original bytes.

Recommended worker status values:

- `completed`
- `timeout`
- `error`
- `not-run`
