okstra 0.146.1 → 0.147.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/README.md +2 -2
  2. package/docs/architecture/storage-model.md +8 -7
  3. package/docs/architecture.md +17 -11
  4. package/docs/cli.md +2 -2
  5. package/docs/project-structure-overview.md +16 -14
  6. package/package.json +1 -1
  7. package/runtime/BUILD.json +2 -2
  8. package/runtime/agents/workers/report-writer-worker.md +10 -10
  9. package/runtime/bin/okstra-render-final-report.py +4 -4
  10. package/runtime/bin/okstra-render-report-views.py +100 -12
  11. package/runtime/prompts/lead/okstra-lead-contract.md +4 -4
  12. package/runtime/prompts/lead/report-writer.md +15 -11
  13. package/runtime/prompts/profiles/_common-contract.md +13 -7
  14. package/runtime/prompts/profiles/improvement-discovery.md +3 -1
  15. package/runtime/python/okstra_ctl/final_report_schema.py +37 -12
  16. package/runtime/python/okstra_ctl/render_final_report.py +136 -28
  17. package/runtime/python/okstra_ctl/report_contract.py +124 -0
  18. package/runtime/python/okstra_ctl/report_finalize.py +1 -1
  19. package/runtime/python/okstra_ctl/report_html/__init__.py +10 -0
  20. package/runtime/python/okstra_ctl/report_html/common.py +50 -0
  21. package/runtime/python/okstra_ctl/report_html/models.py +59 -0
  22. package/runtime/python/okstra_ctl/report_html/render.py +69 -0
  23. package/runtime/python/okstra_ctl/report_html/router.py +40 -0
  24. package/runtime/python/okstra_ctl/report_html/view_models/__init__.py +1 -0
  25. package/runtime/python/okstra_ctl/report_html/view_models/change_impact_analysis.py +38 -0
  26. package/runtime/python/okstra_ctl/report_html/view_models/error_analysis.py +48 -0
  27. package/runtime/python/okstra_ctl/report_html/view_models/feature_analysis.py +38 -0
  28. package/runtime/python/okstra_ctl/report_html/view_models/final_verification.py +46 -0
  29. package/runtime/python/okstra_ctl/report_html/view_models/implementation.py +46 -0
  30. package/runtime/python/okstra_ctl/report_html/view_models/implementation_planning.py +102 -0
  31. package/runtime/python/okstra_ctl/report_html/view_models/improvement_discovery.py +42 -0
  32. package/runtime/python/okstra_ctl/report_html/view_models/project_analysis.py +54 -0
  33. package/runtime/python/okstra_ctl/report_html/view_models/release_handoff.py +53 -0
  34. package/runtime/python/okstra_ctl/report_html/view_models/requirements_discovery.py +54 -0
  35. package/runtime/python/okstra_ctl/report_html/visualizations.py +113 -0
  36. package/runtime/python/okstra_ctl/report_view_artifacts.py +4 -1
  37. package/runtime/python/okstra_ctl/report_views.py +15 -43
  38. package/runtime/python/okstra_ctl/run.py +12 -6
  39. package/runtime/python/okstra_ctl/schema_excerpt.py +7 -17
  40. package/runtime/schemas/final-report-v2.0.schema.json +3923 -0
  41. package/runtime/templates/reports/final-report-v2.template.md +66 -0
  42. package/runtime/templates/reports/html/assets/base.css +38 -0
  43. package/runtime/templates/reports/html/assets/base.js +5 -0
  44. package/runtime/templates/reports/html/base.template.html +65 -0
  45. package/runtime/templates/reports/html/macros/forms.html +47 -0
  46. package/runtime/templates/reports/html/macros/layout.html +19 -0
  47. package/runtime/templates/reports/html/macros/visualizations.html +17 -0
  48. package/runtime/templates/reports/html/tasks/change-impact-analysis.template.html +40 -0
  49. package/runtime/templates/reports/html/tasks/error-analysis.template.html +40 -0
  50. package/runtime/templates/reports/html/tasks/feature-analysis.template.html +40 -0
  51. package/runtime/templates/reports/html/tasks/final-verification.template.html +39 -0
  52. package/runtime/templates/reports/html/tasks/implementation-planning.template.html +47 -0
  53. package/runtime/templates/reports/html/tasks/implementation.template.html +40 -0
  54. package/runtime/templates/reports/html/tasks/improvement-discovery.template.html +29 -0
  55. package/runtime/templates/reports/html/tasks/project-analysis.template.html +57 -0
  56. package/runtime/templates/reports/html/tasks/release-handoff.template.html +36 -0
  57. package/runtime/templates/reports/html/tasks/requirements-discovery.template.html +37 -0
  58. package/runtime/validators/validate-report-views.py +86 -4
  59. package/runtime/validators/validate-run.py +62 -9
  60. package/runtime/validators/validate_improvement_report.py +55 -0
  61. package/src/commands/lifecycle/install.mjs +18 -13
  62. package/src/commands/report/finalize.mjs +2 -3
  63. package/src/commands/report/render-final-report.mjs +4 -2
  64. package/src/commands/report/render-views.mjs +8 -8
package/README.md CHANGED
@@ -217,7 +217,7 @@ Major workflow changes added to `main` after 0.8.0:
217
217
  - **Phase 6 plan-body verification (implementation-planning only)** — Immediately after the report-writer worker drafts the final report and before the user approval gate, the lead performs one post-verification round. It extracts `P-Opt-*`, `P-Step-*`, `P-Dep-*`, `P-Val-*`, and `P-Rb-*` plan items from the synthesized `## 5.5` implementation plan deliverables and asks every analyzer worker for an `AGREE`, `DISAGREE(a-e)`, or `SUPPLEMENT` verdict. The aggregate result is `passed`, `passed-with-dissent`, `blocked-by-disagreement`, or `aborted-non-result`. The frontmatter `approved` field is always published as `false`; a blocking result keeps it false and becomes a row in `## 1. Clarification Items`. For fast iteration, opt out with `--no-plan-verification`. Contract details: the "Plan-body verification mode" section of [`prompts/lead/convergence.md`](prompts/lead/convergence.md) and [`docs/cli.md#--no-plan-verification`](docs/cli.md#--no-plan-verification).
218
218
  - **Brief as translation layer + Step 6.5 reporter batch confirmation** — `okstra-brief-gen` converts external input—an issue ticket, requirements document, or user message—verbatim and marks okstra-added content as labeled augmentation. Step 6.5 asks the user to confirm in one batch whether that conversion changed meaning and records the result in `Reporter Confirmations`. Every analysis profile requires this section before phase analysis begins; `validators/validate-brief.py` enforces the requirement.
219
219
  - **Artifact-home rule (`.okstra/`)** — `<project>/.okstra/` is the only project artifact root owned by okstra. Anything outside this root is not okstra memory and may be read only when explicitly cited in Source Material or Reporter Confirmations. Writing outside the root requires the same explicit requested path. Internal equivalents are `glossary.md` for terminology and `decisions/<NNNN>-<slug>.md` for decision records, evaluated during `implementation-planning`.
220
- - **Self-contained HTML final-report view** — After Phase 7 writes `final-report-<task-type>-<seq>.md`, `okstra render-views` automatically creates a sibling self-contained HTML view in the same `reports/` directory, with inline CSS/JavaScript and no external URLs. Its `Export user response` button serializes `## 1. Clarification Items` responses to `runs/<task-type>/user-responses/user-response-<task-type>-<seq>.md` for the next phase. View generation never changes the source Markdown.
220
+ - **Separate AI and human final-report views** — New runs write `final-report-<task-type>-<seq>.data.json` against `schemas/final-report-v2.0.schema.json`. Phase 7 independently derives compact AI handoff Markdown with `templates/reports/final-report-v2.template.md` and a task-specific, self-contained human HTML view from that same data. Each of the ten task types owns a dedicated template under `templates/reports/html/tasks/`; the HTML emphasizes the task's decisions, findings, visualizations, evidence, and next actions instead of exposing worker discussion as the main narrative. CSS / JavaScript remain inline, print and no-JavaScript reading are supported, and `Export user response` writes the next-phase sidecar. Existing schema v1 data and quick Markdown reports retain the legacy conditional renderer.
221
221
  - **`improvement-discovery` task type (sidetrack entry point)** — Within a codebase scope and priority-lens allowlist, multi-worker consensus produces N improvement candidates, with a default of eight and a hard cap of 12. This is a sidetrack entry point outside `PHASE_SEQUENCE`; the user selects candidates and starts each under a new task ID with `requirements-discovery`, `implementation-planning`, or `error-analysis`. Lens enum SSOT: [`scripts/okstra_ctl/improvement_lenses.py`](scripts/okstra_ctl/improvement_lenses.py). Output section: `## 5.9 Improvement Candidates` (11-column table). Validator: [`validators/validate_improvement_report.py`](validators/validate_improvement_report.py).
222
222
  - **Read-only analysis task types (independent sidetracks)** — `project-analysis` maps the current project's components, dependencies, entry points, data stores, external systems, and feature index. `feature-analysis` traces one existing feature through flows, domain rules, state changes, integrations, and test coverage. `change-impact-analysis` maps the blast radius of a proposed change across preserved behavior, dependencies, tests, and operations. These are independent sidetracks outside `PHASE_SEQUENCE`, not lifecycle phases. No edits, tests, builds, migrations, or deployments are allowed against the target project. Start analysis runs through `/okstra-run`; its wizard owns target and evidence collection before calling the internal Node render command. The final report remains immutable: the HTML `Analysis Review` records accept, revision, or reject in a user-response sidecar. A revision request prioritizes a same-task, same-type full rerun; that rerun reanalyzes the whole confirmed scope and resolves every affected report ID instead of patching only the disputed rows. Input details: [`docs/cli.md`](docs/cli.md#analysis-sidetrack-task-types).
223
223
 
@@ -234,7 +234,7 @@ Major workflow changes added to `main` after 0.8.0:
234
234
  | `npx -y okstra@latest config <get\|set\|unset\|show> [key] [value] [--scope project\|global\|all]` | Read or write okstra settings. The current key is `pr-template-path`, which updates `prTemplatePath` in project.json or `~/.okstra/config.json` |
235
235
  | `npx -y okstra@latest memory <add\|list\|search\|show\|archive>` | Store, search, and archive global conversation memory in `~/.okstra/memory-book` |
236
236
  | `npx -y okstra@latest context-cost <task-key\|task-root> [--project-root <path>]` | Estimate lead, worker, and report-writer context/read costs for a task bundle |
237
- | `npx -y okstra@latest render-views <final-report.md>` | Regenerate the self-contained sibling HTML view for one final-report Markdown file (Phase 7 step 1.5; idempotent) |
237
+ | `npx -y okstra@latest render-views <final-report.data.json\|final-report.md>` | Render schema v2 data directly with its task-specific human template, or use the schema v1 / quick-report compatibility path (Phase 7 step 1.5; idempotent) |
238
238
  | `npx -y okstra@latest token-usage ...` | Collect and substitute run token usage through the installed Python token-usage CLI wrapper |
239
239
  | `npx -y okstra@latest uninstall` | Remove the runtime and skills while preserving user data such as `recent.jsonl` and `projects/` |
240
240
  | `npx -y okstra@latest uninstall --purge -y` | Remove all assets, including user data |
@@ -39,7 +39,9 @@ The task manifest, task index, instruction set, runs, and history are collected
39
39
  │ │ ├── antigravity-worker-prompt-<task-type>-<seq>.md
40
40
  │ │ └── report-writer-worker-prompt-<task-type>-<seq>.md
41
41
  │ ├── reports/
42
- │ │ └── final-report-<task-type>-<seq>.md
42
+ │ │ ├── final-report-<task-type>-<seq>.data.json
43
+ │ │ ├── final-report-<task-type>-<seq>.md
44
+ │ │ └── final-report-<task-type>-<seq>.html
43
45
  │ ├── design-prep-requests/ # implementation-planning only; deterministic Okstra-owned drafts
44
46
  │ ├── design-prep-inputs/ # implementation-planning only; append-only user/wizard responses
45
47
  │ ├── status/
@@ -79,8 +81,9 @@ The representative files below are all relative to the resolved run directory (`
79
81
 
80
82
  Unless `--render-only` is used, the handed-off Claude session typically adds the following result files to the current run.
81
83
  - `sessions/claude-resume-<task-type>-<seq>.sh`
84
+ - `reports/final-report-<task-type>-<seq>.data.json` *(schema v2 source of truth for both audiences; schema v1 remains readable for compatibility)*
82
85
  - `reports/final-report-<task-type>-<seq>.md`
83
- - `reports/final-report-<task-type>-<seq>.html` *(Phase 7 deterministic post-processing: self-contained HTML for human reviewers, with inline CSS/JS)*
86
+ - `reports/final-report-<task-type>-<seq>.html` *(Phase 7 deterministic post-processing: task-specific, self-contained HTML for human reviewers, with inline CSS/JS)*
84
87
  - `design-prep-requests/design-prep-request-<seq>-PREP-<NNN>.md` *(implementation-planning only: deterministic AI-prepared request for a `provisional` or `blocked` design item)*
85
88
  - `design-prep-inputs/design-prep-input-<seq>-PREP-<NNN>-r<revision>-<uuid>.md` *(implementation-planning only: append-only confirmed user/wizard response)*
86
89
  - `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`)*
@@ -98,9 +101,7 @@ Design-preparation storage has three separate authorities:
98
101
  | `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. |
99
102
 
100
103
  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.
101
- The final result files (`final-report` MD / status) are not files that `okstra` creates by saving stdout.
102
- They are results written directly into the current run by Claude, based on the task bundle prepared by `okstra`.
103
- The self-contained HTML view is generated deterministically from a single final-report MD input by `okstra render-views <final-report.md>` (Phase 7 step 1.5). Generating the view does not modify the original MD.
104
+ The final result files are not created by saving worker stdout. The report writer writes `final-report-<task-type>-<seq>.data.json`; Phase 7 derives the Markdown and HTML siblings deterministically. New bundles use `schemas/final-report-v2.0.schema.json` and `templates/reports/final-report-v2.template.md`: AI handoff Markdown and task-specific human HTML are rendered independently from the same v2 data.json. Existing schema v1 data uses `schemas/final-report-v1.0.schema.json` and the legacy `templates/reports/final-report.template.md` compatibility path. Rendering never modifies the source data or a pre-existing Markdown input.
104
105
  By contrast, `sessions/claude-resume-<task-type>-<seq>.sh` is an interruption-recovery helper that `okstra` creates before launching Claude.
105
106
 
106
107
  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`).
@@ -314,8 +315,8 @@ After `okstra` runs, Claude should follow this default sequence when reading the
314
315
  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.
315
316
  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.
316
317
  12. Collect a result or terminal status for each required worker.
317
- 13. Unless the brief requires a more specific format, write the final Markdown report using the `final-report-template.md` structure.
318
- 14. Save the result directly to `reports/final-report-<task-type>-<seq>.md` for the current run and, if necessary, 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.
318
+ 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.
319
+ 14. Run Phase 7 so the AI handoff Markdown and task-specific human HTML are independently derived, 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.
319
320
 
320
321
  Convergence keeps its decision trail under the run's `state/` directory:
321
322
 
@@ -20,7 +20,7 @@ Its core capabilities at a glance are:
20
20
  - **Required team contract**: The `Required workers:` block in each phase profile is authoritative for the roster. General analysis phases use Claude/Codex analyzers plus a report writer by default, while Antigravity is included only when allowed by both the profile and `--workers`. Lead-oriented phases such as `release-handoff` have separate rosters.
21
21
  - **User-home install + project-local task bundles**: One `npx okstra@latest install` command installs the runtime (`~/.okstra/{lib/python, bin, templates, prompts}`) and installs public skills to `~/.agents/skills/` by default. If `~/.claude` exists, it also installs Claude skills and four worker agent definitions (`~/.claude/agents/*-worker.md`). Only user entry-point skills are exposed in the skill list; lead/support operating contracts are installed as runtime resources under `~/.okstra/prompts/` and are not discoverable as skills. Global conversation memory is stored separately from projects under `~/.okstra/memory-book/`. Task bundles and discovery metadata are stored under `.okstra/` in the target project. **In addition, `<PROJECT_ROOT>/.claude/settings.local.json` is provisioned as a symlink to `~/.okstra/templates/settings.local.json`** (`okstra setup` or `okstra-ctl` prepare manages it idempotently; if a regular file already existed, it is preserved as `.bak.<timestamp>` before replacement).
22
22
  - **Resume and clarification**: Supports resuming the same task and responding to follow-up questions from the lead through `--task-key`, `--resume-clarification`, and `--clarification-response`.
23
- - **Derived views and telemetry**: Provides final-report data.json Markdown Report View Model self-contained HTML view, worker error sidecars, wrapper log sidecars, and token-usage/cost accounting.
23
+ - **Dual-audience derived views and telemetry**: For schema v2, derives AI handoff Markdown and task-specific human-facing HTML independently from the same v2 data.json. Schema v1 and quick Markdown reports keep their compatibility renderer. Worker error sidecars, wrapper log sidecars, and token-usage/cost accounting remain separate audit inputs.
24
24
 
25
25
  Claude lead owns judgment policy and worker orchestration. `okstra` focuses on preparing the structured input bundle and output skeleton Claude needs to work reliably.
26
26
 
@@ -135,7 +135,7 @@ Runtime entry points are consolidated in Python packages. Bash and skills only c
135
135
 
136
136
  - `prompts/launch.template.md` — lead prompt template.
137
137
  - `prompts/profiles/*.md` — ten task-type profiles: the six lifecycle profiles (`requirements-discovery`, `error-analysis`, `implementation-planning`, `implementation`, `final-verification`, `release-handoff`) plus `improvement-discovery`, `project-analysis`, `feature-analysis`, and `change-impact-analysis` sidetracks.
138
- - `templates/project-docs/task-index.template.md` · `templates/reports/final-report.template.md` · `templates/reports/settings.template.json` — runtime render inputs.
138
+ - `templates/project-docs/task-index.template.md` · `templates/reports/final-report.template.md` · `templates/reports/final-report-v2.template.md` · `templates/reports/html/` · `templates/reports/settings.template.json` — runtime render inputs. The unversioned Markdown template is the schema v1 compatibility template; the v2 Markdown and task-specific HTML trees are separate audiences.
139
139
  - `<PROJECT_ROOT>/.okstra/project.json` — project self-registration. Created/verified automatically on the first okstra.sh run; when `--project-root` is omitted, PROJECT_ROOT is resolved through ancestors / `git toplevel`.
140
140
 
141
141
  ### Support assets (not referenced at runtime)
@@ -733,8 +733,12 @@ Resume decision rules:
733
733
 
734
734
  ## Final report structure
735
735
 
736
- The default final report template is `templates/reports/final-report.template.md`.
737
- Final reports written by Claude should use the following structure unless the brief's augmentation requires a more specific format.
736
+ New task bundles use `schemas/final-report-v2.0.schema.json` as the final-report data contract. The report writer authors one data.json source of truth, including the required `humanSummary` and exactly one task-type deliverable. Two renderers consume it independently:
737
+
738
+ - `templates/reports/final-report-v2.template.md` produces compact AI handoff Markdown in a fixed order: handoff summary, clarifications, evidence ledger, one task deliverable, cross-verification audit, execution audit, and token/cost audit. Human narrative fields are excluded from this artifact.
739
+ - `templates/reports/html/tasks/<task-type>.template.html` produces the task-specific human-facing HTML. It leads with a plain-language decision summary and presents the selected task's findings, diagrams, tables, evidence, and actions. Worker execution and convergence detail stays in a visually subordinate audit section.
740
+
741
+ The legacy schema v1 contract remains supported by `schemas/final-report-v1.0.schema.json` and `templates/reports/final-report.template.md`. Its Markdown structure is:
738
742
 
739
743
  - `## Verdict Card` — **mandatory top section**. Five rows: Final Conclusion / Verdict Token / Direction / Approval Required? / Next Step. The Verdict Token / Direction / Next Step cells must byte-match the authoritative cells in body §2 (execution status) and §6 (next steps).
740
744
  - (Optional) `## Reader Summary` — a five-row table rendered immediately below the Verdict Card only when data.json has `readerSummary`: decision (`decision`) / action required from a human (`humanActionRequired`) / blocking items (`blockingItems`) / safe-to-skip items (`safeToSkip`) / recommended command (`recommendedCommand`). When present, all five fields are required (schema `required`), and it contains only a summary rather than repeating raw evidence tables. Older data.json files without it continue to render unchanged.
@@ -756,17 +760,19 @@ Write the actual Markdown report body to the file instead of metadata about save
756
760
 
757
761
  ## Final report views (HTML)
758
762
 
759
- The Phase 7 `render-views` step deterministically generates a self-contained HTML view from a single final-report MD input. The lead reaches it through `okstra report-finalize`, which runs the whole Phase 7 sequence (`scripts/okstra_ctl/report_finalize.py` — shared with the Codex lead adapter).
763
+ The Phase 7 `render-views` step accepts either a final-report data.json or its Markdown sibling. For schema v2, it locates and validates `final-report-<task-type>-<seq>.data.json`, selects the task type fail-closed, and renders HTML directly from the structured data. It does not parse the AI Markdown back into a human model. The lead reaches this step through `okstra report-finalize`, which owns the shared Phase 7 sequence in `scripts/okstra_ctl/report_finalize.py`.
760
764
 
761
- - `reports/final-report-<task-type>-<seq>.html` — self-contained HTML for human reviewers. CSS / JS are embedded inline (zero external URLs), with system-color dark mode, sticky header, and print support. Reviewers can fill in decision inputs for §1 `C-*` rows—checkboxes, selects, and textareas—in the browser, then generate sidecar Markdown with the `Export user response` button.
762
- - **Reader Summary dashboard + reader mode**: Renders a top-level dashboard based on `readerSummary` (falling back to `verdictCard` when absent) and offers `Action` / `Audit` / `Full` reader-mode toggles. The default is `Action`, which shows only Reader Summary / Verdict Card / Clarification Items / Recommended Next Steps / Follow-up Tasks. Audit sections such as Evidence, Cross Verification, Execution Status, Token Usage, Plan Body Verification, and Round History are expanded in `Audit` / `Full`.
765
+ - `reports/final-report-<task-type>-<seq>.html` — always generated for schema v2 with one of ten dedicated task templates. It includes an accessible summary, task-specific prose, tables and inline SVG diagrams, evidence references, decisions, and next actions. CSS / JS are embedded inline with no external assets; print and no-JavaScript fallback content preserve the essential information.
766
+ - **Human summary**: `humanSummary` is the sole v2 top-level human summary contract. It is not copied into AI Markdown. Each task view decides how to present it together with the task deliverable instead of sharing a generic dashboard body.
767
+ - **Audit isolation**: worker execution, convergence, and token/cost material remain available for traceability but are subordinate to the user's findings and decisions. They never replace the task analysis narrative.
768
+ - **Schema v1 compatibility**: existing v1 data and quick Markdown reports keep the legacy conditional renderer, including `readerSummary`, reader modes, and the original Markdown-parsing path.
763
769
  - **`C-*` select option order**: The `Recommended:` answer from `Expected form` is always the **first option**, followed by the `Alternatives:` items relabelled consecutively as `(a)`, `(b)`, and so on (the original character labels are not retained).
764
770
 
765
771
  Entry points:
766
772
 
767
- - Single Python reference: `scripts/okstra_ctl/report_views.py` (`build_report_view_model(...)`, `render_report_view_model(..., css, js)`, `render_html(..., css, js)`, `serialize_user_response(...)`). The HTML JavaScript function `buildUserResponseMarkdown` is **byte-identical** to Python `serialize_user_response` (automatically verified by a Node `vm.runInThisContext` unit test).
768
- - CLI: `scripts/okstra-render-report-views.py <final-report.md>` or delegated Node wrapper `bin/okstra render-views <md>`.
769
- - Validation: `validators/validate-report-views.py` checks form-control placement in the HTML, absence of external URLs, stale source digests, and Response ID parity (`C-*` HTML).
773
+ - Python routing references: `scripts/okstra_ctl/report_views.py` owns the version split; `scripts/okstra_ctl/report_html/router.py` maps all v2 task types to dedicated view-model builders and templates. The HTML JavaScript response serializer remains byte-identical to Python `serialize_user_response`.
774
+ - CLI: `scripts/okstra-render-report-views.py <final-report.data.json|final-report.md>` or delegated Node wrapper `bin/okstra render-views <path>`.
775
+ - Validation: `validators/validate-report-views.py` checks schema/source/template digests, required human fields, form controls, external assets, SVG/table ID parity, and Response ID parity. The v2 renderer rejects an unknown task type instead of falling back to a generic page.
770
776
  - User-response sidecar schema source of truth: `templates/reports/user-response.template.md`.
771
777
 
772
778
  Generating a view never modifies the original final-report MD.
@@ -826,7 +832,7 @@ Tokens used in each run are collected from lead/worker session transcripts and w
826
832
  Entry points that enforce whether phase artifacts may be published:
827
833
 
828
834
  - `validators/validate-workflow.sh` — integrated phase-contract validation.
829
- - `validators/validate-run.py` — run-level final-report body contract: requires a Verdict Card; enforces evidence anchors in all four `rationale` fields (`_validate_rationale_evidence`); rejects deprecated §6.1/§6.2/§5.5.8/§5.5.9 Open Questions; cross-checks Plan Body Verification gate × Approval markers; blocks Token Usage sentinel/zero values; requires worker-result audit sidecars; requires the incremental re-verification audit block (`_check_incremental_audit_block`—when data.json has `incrementalDecision.mode == "incremental"`, `### 0.1 Incremental Re-Verification Scope` plus `Re-verified stages` / `Carried-forward stages` rows are mandatory).
835
+ - `validators/validate-run.py` — version-aware run-level final-report contract. For schema v2 it validates the compact AI handoff heading order and rejects human-only fields in Markdown; task deliverables and cross-field rules are validated from data.json. For schema v1 it retains the Verdict Card, Index, numbered-section, Plan Body Verification, approval, and token-table checks. Both versions require rationale evidence, consistent task verdicts, worker-result audit sidecars, and phase-safe execution evidence.
830
836
  - `validators/validate-report-views.py` — checks form-control placement in the self-contained HTML view, absence of external URLs, stale source digests, and Response ID parity (`C-*` ↔ HTML).
831
837
  - `validators/validate-brief.py` — enforces the brief schema (frontmatter, presence of `Reporter Confirmations`, root parent-id self rule, slug conventions, and so on). `bash validators/validate-brief.sh <brief.md>` is a thin wrapper.
832
838
 
package/docs/cli.md CHANGED
@@ -675,7 +675,7 @@ chmod +x ~/.local/bin/okstra-ctl
675
675
  | View in-progress runs | `okstra-ctl tail active` |
676
676
  | Show metadata for one run | `okstra-ctl show <runId-or-prefix>` |
677
677
  | Print the result-report path | `okstra-ctl open <runId-or-prefix>` |
678
- | Regenerate a final-report HTML view | `okstra render-views <final-report.md>` |
678
+ | Regenerate a final-report HTML view | `okstra render-views <final-report.data.json\|final-report.md>` |
679
679
  | Rerun one run | `okstra-ctl rerun <runId-or-prefix> --yes` |
680
680
  | Rerun multiple filtered runs | `okstra-ctl rerun --filter --project X --status failed --yes` |
681
681
  | Rerun the latest matching run | `okstra-ctl rerun last --project X --task-group Y --yes` |
@@ -732,7 +732,7 @@ The `okstra` Node CLI (`bin/okstra`) provides both installer/admin commands and
732
732
  | `okstra codex-dispatch --project-root <dir> --run-manifest <path> [--workers codex,antigravity,report-writer]` | Read a run manifest prepared by `codex-run` and execute the Codex-side supported worker subset. Without `--workers`, unsupported roster members such as `claude` are skipped; explicitly requesting one fails. The report writer requires opt-in with `--enable-codex-report-writer --report-writer-codex-model <model>`. On success, it automatically performs token-usage substitution, HTML view rendering, follow-up task-stub generation, and run validation |
733
733
  | `okstra team dispatch --project-root <dir> --run-manifest <path> [--workers <csv>] [--jobs-file <path>] [--dry-run]` / `okstra team await --project-root <dir> --run-manifest <path> [--json]` / `okstra team teardown --project-root <dir> --run-manifest <path> [--dry-run] [--json]` | Read a `leadRuntime=external` run manifest and dispatch, await, or tear down tmux-pane workers. If a tmux pane cannot be created, gracefully degrade to the CLI wrapper and record the fallback in `workerDispatches[].degradedFrom` |
734
734
  | `okstra report-finalize --project-root <dir> --run-manifest <path> --report <final-report.md>` | Run the whole Phase 7 post-report sequence in its contractual order: `token-usage` → `render-views` → `spawn-followups` → `validate-run`. Stops at the first non-zero exit and names the failing step, then prints a per-step `[ok]` / `[FAIL]` / `[skip]` summary on stderr so the outcome is legible without parsing the JSON payload. Every step is idempotent, so re-running after a fix is safe — but `--only <step>` (repeatable) reruns just the named steps in contractual order, which matters because `validate-run` is the step that usually fails and retrying it otherwise repeats the three steps before it at full token and wall-clock cost. This is the same code path (`scripts/okstra_ctl/report_finalize.py`) the Codex lead adapter runs automatically after its report-writer completes, so a Claude-led and a Codex-led run finalize identically. `--workspace-root` is owned by the Node wrapper. Prefer this over invoking the four steps individually |
735
- | `okstra render-views <final-report.md>` | The Phase 7 `render-views` step, runnable on its own: deterministically create a human-facing self-contained sibling `*.html` view from one final-report Markdown file after token substitution. The source Markdown is unchanged. The Node delegation wrapper calls `scripts/okstra-render-report-views.py`; `validators/validate-report-views.py` verifies form-control placement, absence of external URLs, stale source digests, and Response ID parity |
735
+ | `okstra render-views <final-report.data.json\|final-report.md>` | The Phase 7 `render-views` step, runnable on its own. Schema v2 data is rendered directly (contract: `schemas/final-report-v2.0.schema.json`) into an always-generated, task-specific human HTML sibling while `templates/reports/final-report-v2.template.md` independently owns the AI handoff Markdown. Passing the Markdown sibling locates the same v2 data.json. Schema v1 and quick reports keep the legacy conditional renderer. The Node wrapper calls `scripts/okstra-render-report-views.py`; `validators/validate-report-views.py` verifies source/schema/template digests, required human fields, form controls, external assets, diagram/table ID parity, and Response ID parity |
736
736
  | `okstra design-prep <list\|show\|write>` | Review AI-prepared implementation design requests, inspect their effective confirmed response, or append a confirmed user/wizard response without editing the planning report |
737
737
  | `okstra wizard <init\|step\|render-args\|confirmation\|outcome> --state-file <path>` | Interactive input state machine for okstra-run, implemented by `okstra_ctl.wizard`. Seed a state file with `init`, then repeatedly call `step --answer <val>` to receive the next `Prompt` JSON. `--answer` is **required**; use `--no-submit` to peek at the next prompt without submitting a response. A `pick` with more choices than the host picker can display keeps `kind: "pick"` but adds `presentation: "numbered-text"`; render every option as a numbered Markdown list and submit the user's 1-based number, exact value, or exact label. Invalid, out-of-range, and ambiguous answers re-prompt without dropping choices. `render-args` returns the final `render-bundle` argument map, and `confirmation` returns the user echo block. On a completed wizard, `outcome` returns `renderArgs`, `persistActions`, and `confirmationText` together; project/global release-handoff PR-template persistence appears as `persistActions[].command == "config.set"`. For an `implementation` task type, `stage_pick` follows `approved_plan_pick` and selects the stage before `executor_pick`. The brief step appears only for entry task types—requirements-discovery, error-analysis, improvement-discovery, project-analysis, feature-analysis, and change-impact-analysis. Analysis inputs use `feature_evidence_pick` / `feature_evidence`, `project_evidence_pick` / `project_evidence`, and `analysis_target_pick` / `analysis_target`; a revision-requested report prioritizes its same-task, same-type rerun. Downstream lifecycle phases automatically carry the manifest brief, with a three-option `brief_carry` fallback when none is registered; `release-handoff` has no brief and enters multi-select `handoff_stage_pick` for eligible stage groups or the whole task |
738
738
  | `okstra token-usage ...` | Wrap the installed `okstra-token-usage.py` to collect and substitute run token usage. Session JSONL is incrementally scanned by default through a byte-cursor cache at `$OKSTRA_HOME/cache/token-usage/`; `--no-cache` bypasses the cache and forces a full rescan as an accuracy fallback |
@@ -30,7 +30,7 @@ Current baseline:
30
30
  - lifecycle: `requirements-discovery → error-analysis → implementation-planning → implementation → final-verification → release-handoff`
31
31
  - installed skills: 8
32
32
  - worker agents: `claude`, `codex`, `antigravity`, `report-writer`
33
- - final report SSOT: `schemas/final-report-v1.0.schema.json` + `*.data.json`
33
+ - final report SSOT: current `schemas/final-report-v2.0.schema.json` + `*.data.json`; schema v1 remains a compatibility contract
34
34
 
35
35
  Design principles:
36
36
 
@@ -186,8 +186,8 @@ Runtime/install asset changes follow this checklist:
186
186
  | `convergence` | `src/commands/execute/convergence.mjs` | Internal admin CLI for the deterministic Phase 5.5 convergence engine (`seed`/`plan-round`/`apply-round`/`apply-critic-gaps`/`finalize`/`validate`/`example`; Python: `okstra_ctl.convergence`) |
187
187
  | `plan-items` | `src/commands/execute/plan-items.mjs` | Internal admin CLI for deterministic plan-body item extraction and exact-match validation (`extract`/`validate`; Python: `okstra_ctl.plan_items_cli`) |
188
188
  | `report-finalize` | `src/commands/report/finalize.mjs` | Run the whole Phase 7 post-report sequence in contractual order (Python: `okstra_ctl.report_finalize`) — the single reference point shared with the Codex lead adapter |
189
- | `render-views` | `src/commands/report/render-views.mjs` | Generate the self-contained HTML report view |
190
- | `render-final-report`, `inject-report-index` | `src/commands/report/*.mjs` | Render final-report Markdown from data.json and inject top-of-report index anchors |
189
+ | `render-views` | `src/commands/report/render-views.mjs` | Render schema v2 data with its task-specific human template, or use the schema v1 / quick-report compatibility view |
190
+ | `render-final-report`, `inject-report-index` | `src/commands/report/*.mjs` | Render version-selected AI handoff Markdown from data.json; v1 index injection remains compatibility-only |
191
191
  | `wizard` | `src/commands/execute/wizard.mjs` | Drive the `okstra-run` interactive state machine, including the final outcome envelope |
192
192
  | `token-usage` | `src/commands/execute/token-usage.mjs` | Wrap installed Python token usage CLI |
193
193
  | `spawn-followups`, `error-log` | `src/commands/execute/*.mjs` | Follow-up task bundle creation and run error-log append helpers |
@@ -218,8 +218,8 @@ Top-level scripts:
218
218
  | `okstra-claude-exec.sh`, `okstra-codex-exec.sh`, `okstra-antigravity-exec.sh` | Worker CLI wrappers with log/status sidecars |
219
219
  | `okstra-wrapper-status.py` | Heartbeat sidecar writer used by worker wrappers |
220
220
  | `okstra-token-usage.py` | Token usage CLI entrypoint |
221
- | `okstra-render-final-report.py` | Render final-report Markdown from data.json |
222
- | `okstra-render-report-views.py` | Render self-contained HTML views from final-report Markdown |
221
+ | `okstra-render-final-report.py` | Render version-selected final-report Markdown from data.json |
222
+ | `okstra-render-report-views.py` | Render schema v2 task-specific HTML directly from data.json, or a legacy view from schema v1 / quick Markdown |
223
223
  | `okstra-error-log.py` | Normalize worker/lead error sidecars |
224
224
  | `okstra-spawn-followups.py` | Follow-up spawning helper |
225
225
  | `okstra-trace-cleanup.sh` | tmux okstra pane cleanup (worker-agent + trace, excluding the lead pane), called by the lead at every worker round boundary — not once per phase; `--keep <substr>` (repeatable) spares panes whose title contains the substring, which is how an in-flight `report-writer-worker` survives a boundary; `--list` prints what would be reclaimed without killing; the `--reclaim-completed` mode reclaims only trace panes whose `@okstra_status` is terminated (stage=exited) and preserves in-progress panes |
@@ -260,7 +260,7 @@ Important modules:
260
260
  | `qa_commands.py` | QA command deny-list validation for plans |
261
261
  | `conformance.py` | validates task-level Tier 3 manifests, parses `QA-RESULT`, detects diff capability surfaces, and reduces results to PASS/ADVISORY/BLOCKING; DB/HTTP/external non-PASS is user-owned advisory while local IO and contract defects remain blocking, enforced by `scripts/okstra_ctl/conformance.py::decide_conformance_gate` and `validators/validate-run.py::_validate_conformance` |
262
262
  | `pr_template.py` | PR body template resolution for release-handoff |
263
- | `report_views.py`, `render_final_report.py`, `final_report_schema.py` | Final-report data.json Markdown Report View Model self-contained HTML pipeline |
263
+ | `report_views.py`, `render_final_report.py`, `final_report_schema.py` | Versioned final-report contract: schema v2 data independently produces AI handoff Markdown and human HTML; schema v1 keeps the legacy Markdown/view pipeline |
264
264
  | `final_report_paths.py`, `report_view_artifacts.py` | Path-helper SSOT for the final-report markdown/data.json pair and the generated view artifacts (HTML view, user-responses directory) |
265
265
  | `wizard.py` | `okstra-run` prompt state machine; user-facing Korean strings live in `prompts/wizard/prompts.ko.json` |
266
266
  | `wizard_stage_intent.py` | stage-related intent projection of the `okstra-run` wizard output — normalizes whole-task (`__whole_task__`) vs single/multi stage selection into render-args (`resolve_wizard_stage_intent`) |
@@ -351,8 +351,10 @@ Token/cost accounting:
351
351
 
352
352
  | Path | Role |
353
353
  |---|---|
354
- | `templates/reports/final-report.template.md` | Jinja2 final-report Markdown template |
355
- | `templates/reports/report.css`, `report.js` | Inline assets for self-contained HTML report view |
354
+ | `templates/reports/final-report.template.md` | Schema v1 compatibility Markdown template |
355
+ | `templates/reports/final-report-v2.template.md` | Compact schema v2 AI handoff Markdown template |
356
+ | `templates/reports/html/base.template.html`, `html/tasks/*.template.html` | Shared HTML shell plus ten dedicated task templates for human reports; task bodies are not shared |
357
+ | `templates/reports/report.css`, `report.js` | Inline assets for self-contained HTML report views |
356
358
  | `templates/reports/*.template.md` | Inputs, schedule, user-response, settings templates |
357
359
  | `project-analysis-input.template.md`, `feature-analysis-input.template.md`, `change-impact-analysis-input.template.md` | Brief input templates for the three analysis sidetracks |
358
360
  | `user-response.template.md`, `report.js` | Analysis Review sidecar block and the browser control that exports accept/revision/reject without changing the source report |
@@ -364,7 +366,7 @@ Token/cost accounting:
364
366
 
365
367
  ### 4.8 `schemas/`
366
368
 
367
- `schemas/final-report-v1.0.schema.json` is the final-report data.json contract. The report-writer worker writes `final-report-<task-type>-<seq>.data.json`; the renderer produces Markdown from that JSON.
369
+ `schemas/final-report-v2.0.schema.json` is the current final-report data.json contract. The report-writer worker writes `final-report-<task-type>-<seq>.data.json`; independent renderers produce AI handoff Markdown and task-specific human HTML. `schemas/final-report-v1.0.schema.json` remains supported for existing reports and quick-report compatibility.
368
370
 
369
371
  The deterministic convergence inputs are `schemas/convergence-groups-v1.0.schema.json`, `schemas/convergence-round-results-v1.0.schema.json`, and `schemas/convergence-critic-results-v1.0.schema.json`. `tools/build.mjs` syncs the entire source `schemas/` directory to `runtime/schemas/`; these JSON Schema files are runtime contracts, not Markdown publication-inventory entries.
370
372
 
@@ -377,7 +379,7 @@ Optional (v1.0 backward-compatible) top-level keys:
377
379
 
378
380
  | File | Role |
379
381
  |---|---|
380
- | `validate-run.py` | Run/final-report contract validation |
382
+ | `validate-run.py` | Version-aware run/final-report validation: schema v2 AI handoff order + structured data rules, with legacy schema v1 Markdown gates preserved |
381
383
  | `validate-brief.py`, `validate-brief.sh` | Brief frontmatter/body contract validation |
382
384
  | `validate-report-views.py` | HTML view validation (form-control placement / no external URLs / stale source digest / Response ID parity) |
383
385
  | `validate_analysis_report.py` | Cross-field validation for the three read-only analysis reports: frozen target/evidence snapshots, current-code evidence, review-source identity, and exact affected-ID resolution coverage on revision reruns |
@@ -498,15 +500,15 @@ Current report pipeline:
498
500
 
499
501
  1. Analysis workers write worker result files and the separate audit sidecars named by `Audit sidecar path`.
500
502
  2. The lead writes semantic groups; the convergence engine persists working state, per-round plans/results, an optional critic transition, and then a validated `state/convergence-<task-type>-<seq>.json` terminal state: schema v1.3 when newly finalized, or an unchanged historical final schema v1.0, v1.1, or v1.2 returned by `reuse-final`.
501
- 3. Report-writer worker writes `reports/final-report-<task-type>-<seq>.data.json`.
503
+ 3. Report-writer worker writes `reports/final-report-<task-type>-<seq>.data.json` against the current schema v2 contract, including `humanSummary` and one task-type deliverable.
502
504
  4. For implementation-planning, `okstra plan-items extract` creates the complete `P-*` queue, `validate` proves it still matches data.json, and the analyser instances run the separate plan-body verification round.
503
- 5. `scripts/okstra-render-final-report.py` renders Markdown.
505
+ 5. `scripts/okstra-render-final-report.py` renders compact AI handoff Markdown with `templates/reports/final-report-v2.template.md`.
504
506
  6. Token usage substitution fills usage/cost cells.
505
- 7. `scripts/okstra-render-report-views.py` emits the self-contained `.html` view, and run validation checks the final artifacts.
507
+ 7. `scripts/okstra-render-report-views.py` independently selects one of ten dedicated task templates and emits human-facing HTML directly from the same data.json; run validation checks both derived artifacts. Schema v1 and quick Markdown inputs retain their legacy conditional path.
506
508
 
507
509
  For the three analysis sidetracks, the HTML view also exports an immutable-source `## ANALYSIS REVIEW` sidecar. A revision rerun carries that sidecar, reanalyzes the whole confirmed scope, and records one `analysisReviewResolution` row for every affected ID before `validate_analysis_report.py` accepts the result.
508
510
 
509
- The Markdown is derived, not the authoring source. The schema is the contract.
511
+ Both Markdown and HTML are derived, not authoring sources. The schema is the contract.
510
512
 
511
513
  ---
512
514
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "okstra",
3
- "version": "0.146.1",
3
+ "version": "0.147.0",
4
4
  "description": "Multi-agent cross-verification orchestrator runtime + Claude Code skills.",
5
5
  "license": "MIT",
6
6
  "author": "devonshin",
@@ -1,5 +1,5 @@
1
1
  {
2
- "package": "0.146.1",
3
- "builtAt": "2026-08-03T06:04:45.516Z",
2
+ "package": "0.147.0",
3
+ "builtAt": "2026-08-03T08:30:05.489Z",
4
4
  "repoRoot": "/home/runner/work/okstra/okstra"
5
5
  }
@@ -24,7 +24,7 @@ tools: ["Bash", "Read", "Write", "Edit", "Glob", "Grep", "TodoWrite", "WebFetch"
24
24
 
25
25
  You are the canonical author of `runs/<task-type>/reports/final-report-<task-type>-<seq>.data.json` for this run. Claude lead has explicitly delegated file-authorship to you. The lead reviews your output but does not write the file.
26
26
 
27
- The data.json is the **single source of truth**. The renderer (`scripts/okstra-render-final-report.py`) produces the user-facing markdown (`final-report-<task-type>-<seq>.md`) deterministically from it. You do NOT hand-write the markdown. The markdown is regenerated whenever the data.json changes (Phase 7 token substitution, future re-renders).
27
+ The data.json is the **single source of truth** for two audiences. The renderer (`scripts/okstra-render-final-report.py`) produces the AI handoff Markdown (`final-report-<task-type>-<seq>.md`) deterministically from it. Phase 7 produces the human HTML (`final-report-<task-type>-<seq>.html`) through the task-specific HTML renderer. HTML is rendered directly from the data.json; it is not a presentation of the Markdown. You do NOT hand-write either derived artifact. Both are regenerated whenever the data.json changes.
28
28
 
29
29
  If you find yourself thinking "I'll just write the markdown directly" — stop. Write the data.json with your `Write` tool and let the renderer produce the markdown.
30
30
 
@@ -72,22 +72,22 @@ Before writing the data.json, you MUST:
72
72
 
73
73
  For the report writer specifically, the `## Inputs` list always includes:
74
74
 
75
- - `<instruction-set>/final-report-schema.json` — the **per-task-type excerpt** of the data.json schema (scoped to this run's task-type at prep time: other task-types' deliverable blocks and their unreachable `$defs` are stripped). This is the shape you must author. Read this, NOT the full `schemas/final-report-v1.0.schema.json` (it is not in the task bundle and its `schemas/...` path is not resolvable here). Validation still runs against the full schema post-hoc, so the excerpt never relaxes the contract. The excerpt is frozen at prep time and carries the okstra version it was cut from (`x-okstraCutFromVersion`); if the renderer rejects a field the excerpt told you to write, the runtime upgraded mid-run — the renderer's error names the version skew, and the **installed schema wins**.
76
- - `<instruction-set>/final-report-template.md` — the **phase-stripped** Jinja2 template the renderer uses (only this run's §4.x deliverable block remains). Read it to understand which data.json fields appear where in the rendered markdown; do NOT edit it, and do NOT pull the full `templates/reports/final-report.template.md` source.
75
+ - `<instruction-set>/final-report-schema.json` — the **per-task-type excerpt** of schema v2 (other task-types' deliverable blocks and unreachable `$defs` are stripped). This is the shape you must author. Read this, NOT the full `schemas/final-report-v2.0.schema.json` source outside the task bundle. Validation still runs against the installed full schema, so the excerpt never relaxes the contract. The excerpt is frozen at prep time and carries the okstra version it was cut from (`x-okstraCutFromVersion`); if the renderer rejects a field the excerpt told you to write, the installed schema wins.
76
+ - `<instruction-set>/final-report-template.md` — the AI handoff Markdown template. Read it to understand which IDs, routing fields, evidence, task deliverable, and audit blocks appear in the AI artifact; do NOT edit it, and do NOT use it as the human presentation contract.
77
77
  - `templates/reports/i18n/en.json` and `templates/reports/i18n/ko.json`.
78
78
  - Every analysis worker's result file under `worker-results/`.
79
79
  - `state/convergence-<task-type>-<seq>.json` (if present). When present, reproduce its `roundHistory[]`, `round2SkippedReason`, and `finalClassificationCounts` verbatim into the final report's Section 6 Round History sub-table — do not recompute from worker results.
80
80
  - `<instruction-set>/task-brief.md` — the brief this run was prepared from. The lead already lists it under `## Inputs`; what it is FOR is the `## Expected Behavior` / `## Preserved Behavior` / `## Expected Outcome` items, whose `EB-NNN` / `PB-NNN` / `EO-NNN` ids are exactly what `endStateCoverage` maps. Read the instruction-set copy, NOT the project's `.okstra/briefs/...` path from the task manifest — that path is not resolvable here, same as the full schema source. A report that omits an id the brief pinned is rejected by the run validator.
81
81
 
82
- For the carry-in `clarification-response.md` (if present), walk every row of `## 1. Clarification Items` including rows whose `User input` cell is blank a blank cell with `Status=open` is a signal you must surface in the conditional `## 0. Clarification Response Carried In From Previous Run` section (the template's `RENDER_IF` guard activates it when the carry-in path is non-empty). When no carry-in path was provided, OMIT the `## 0.` heading entirely — do NOT write an empty-state stub.
82
+ For a carry-in `clarification-response.md`, reconcile every prior `clarificationItems[]` row, including an open row with blank user input. Record the current status and user decision in data.json; the AI handoff renderer places it under `## Clarification and User Decisions`, while HTML renders any still-open response controls. When no carry-in path was provided, omit `clarificationCarryIn` entirely.
83
83
 
84
84
  Write a Reading Confirmation block to `**Audit sidecar path:**`, per the selected report-writer preamble's `Required reading` section (the main final-report and worker-results files carry no Section 0 heading). If you cannot truthfully confirm a file end-to-end, record a `tool-failure` in the errors sidecar instead of fabricating the report.
85
85
 
86
86
  ## Authoring Contract
87
87
 
88
- You author the final-report data.json (the JSON SSOT). You author it against the `<instruction-set>/final-report-schema.json` excerpt — its `$defs` enumerate every row shape, enum value, and cross-field constraint that applies to this run's task-type. The validator and renderer both consume the **full** `schemas/final-report-v1.0.schema.json` (the excerpt is a faithful task-type-scoped subset of it), so a data.json that satisfies the excerpt is a data.json that validates and renders correctly.
88
+ You author the final-report data.json (the JSON SSOT). You author it against the `<instruction-set>/final-report-schema.json` excerpt — its `$defs` enumerate every row shape, enum value, and cross-field constraint that applies to this run's task-type. The validator and renderers both consume the **full** `schemas/final-report-v2.0.schema.json` (the excerpt is a faithful task-type-scoped subset of it), so a data.json that satisfies the excerpt can independently produce both audience artifacts.
89
89
 
90
- The rendered markdown (`final-report-<task-type>-<seq>.md`) is produced by `scripts/okstra-render-final-report.py` immediately after you write the data.json. The HTML view (`*.html`) is produced from the markdown by the lead's Phase 7 `okstra report-finalize` run, not by you. The data.json is the only file you write; the rest are derived.
90
+ The AI handoff Markdown is an agent-facing ledger: verdict, routing, clarification decisions, evidence, one structured task deliverable, and execution audits. The human HTML is the reader-facing explanation: `humanSummary` plus the selected task block's `userNarrative` and structured facts. Populate both human fields in data.json even though the Markdown intentionally omits their full prose. Worker discussion, convergence mechanics, and token usage belong to audit data and must not be copied into the HTML human main body.
91
91
 
92
92
  Rules (the schema enforces most of these — they are listed here so you know *what* to populate, not *how* to validate):
93
93
 
@@ -95,7 +95,7 @@ Rules (the schema enforces most of these — they are listed here so you know *w
95
95
  - **Source items (worker:item) preservation.** Every `consensus[].sourceItems`, `differences[].workersPosition[].itemId`, and `evidence.primary[].sourceItems` entry MUST carry the worker:item-id pair (e.g. `claude:F-001`, `codex:1.1`, `antigravity:F-3`, or `lead:mcp-1` for lead-only evidence). The schema enforces this via the `SourceItem` regex; bare worker-name lists no longer parse.
96
96
  - **Verdict Card consistency.** `verdictCard.verdictToken` and `verdictCard.direction` MUST byte-match `finalVerdict.verdictToken` / `.direction`; `validators/validate-run.py` diffs both and fails the run on divergence. `verdictCard.nextStep` names the same action as `finalVerdict.nextStep` and `recommendedNextSteps[0].text` but is written as the actionable command the reader runs (e.g. `/okstra-run task-key=… task-type=release-handoff`) where the other two are prose — it is deliberately not a byte copy. Duplicating the compared values across `verdictCard` and `finalVerdict` is intentional so the validator can diff them.
97
97
  - **Error-analysis diagnosis and routing.** When `header.taskType` is `error-analysis`, populate the required `errorAnalysis` object. Copy `errorAnalysis.symptomVerbatim` byte-for-byte from the symptom stated in the brief's `Source Material`; do not paraphrase it. Every `causeCandidates[]` row includes the full `supportingEvidence`, `falsifyingEvidenceChecked`, `confidence`, and `disproveWith` fields. Route `errorAnalysis.routing.nextTaskType=implementation-planning` with `direction=begin-planning`, or route `errorAnalysis.routing.nextTaskType=error-analysis` with `direction=continue-investigation`; no other pairing is valid. `verdictCard.nextStep`, `finalVerdict.nextStep`, the first `recommendedNextSteps` action and command, and the unique `followUpTasks` row whose `origin` is `phase-continuation` MUST all point to the same `errorAnalysis.routing.nextTaskType` target. The schema enforces only the presence of a `phase-continuation` row. Phase validation MUST enforce exact target agreement and uniqueness through `validators/validate-run.py::_validate_error_analysis_consistency`; until that check is implemented and executed, those semantics are contract requirements rather than enforced guarantees.
98
- - **Reader Summary.** Populate `readerSummary` when the schema excerpt exposes it. It is the human-first entrypoint for both Markdown and HTML: one sentence for the decision, one for the human action required, one for blockers, one for audit sections safe to skip on first read, and one runnable recommended command. Do not duplicate raw evidence tables here.
98
+ - **Human narrative.** Populate required `humanSummary` and the selected task block's `userNarrative`. Human-visible analysis facts must not exist only in Markdown; HTML is derived independently and can use only data.json. Keep worker discussion and audit details in `crossVerification`, `executionStatus`, and `tokenUsage`, outside the human narrative fields.
99
99
  - **External QA advisory.** A Tier 3 entry requiring `db`, `http`, or
100
100
  `external` may be non-PASS without changing approval or final verdict. Render
101
101
  its command log row as `tier: 3`, `status: advisory`, keep the observed and
@@ -116,11 +116,11 @@ Rules (the schema enforces most of these — they are listed here so you know *w
116
116
  - Cite file paths and line numbers in every `evidence.primary[].source` / `consensus[].evidence` cell.
117
117
  - Preserve every analysis worker's ticket tagging — every row's `ticketId` field carries the ticket key or the task-fallback. For single-ticket runs, set `ticketCoverage` to `{"singleTicket": "<ticket>"}`. For runs that do not require ticket tagging (`release-handoff`, `final-verification`), set `ticketCoverage` to `{"omit": true}`.
118
118
  - For `requirements-discovery`, `error-analysis`, and `implementation-planning`, populate the top-level `endStateCoverage` with exactly one row per end-state id the brief declares — no more, no fewer. `disposition` is one of `addressed` / `deferred` / `not-applicable` / `blocked`. `addressed` requires a `coveredBy` anchor in THIS phase's own deliverable (requirements-discovery: the routing decision, the fan-out unit id, or the `C-NNN` clarification; error-analysis: the root-cause candidate or the next diagnostic; implementation-planning: the `R-NNN` row); every other disposition requires a `rationale`. Do not author a goal of your own here and do not restate the brief — this table records only how this phase accounted for what the reporter already pinned. When the brief declares no end-state ids (a brief authored before those sections existed), omit the field entirely. **Enforced:** `validators/validate-run.py` `_validate_end_state_coverage`.
119
- - For `implementation-planning`, populate `implementationPlanning.requirementCoverage` with one row per concrete requirement from the brief / packet, using IDs `R-001`, `R-002`, ... in source order. A `covered` row's `coveredBy` MUST name the specific Option Candidate plus Stage/Step that satisfies the requirement. Use `status: "covered"` only when the report's plan actually covers it; use `documented-deviation` only when `coveredBy` states the concrete alternative and the row records non-empty unique `decisionRefs` plus `approvalDisposition`. Each `C-NNN` ref must name a clarification in this report; each `D-NNNN` ref must name a `decisionDrafts[].number`. `approvalDisposition: "accepted"` requires a referenced clarification with `status: answered|resolved` and non-empty `userInput`; `approvalDisposition: "blocked C-NNN"` requires that same-report clarification to be `status: open, blocks: approval`. Otherwise use `gap` or `blocked C-NNN` and ensure the corresponding `Clarification Items` row blocks approval. Do not collapse this into `ticketCoverage`; ticket coverage is not requirement coverage. **Enforced:** `schemas/final-report-v1.0.schema.json` `$defs.ImplementationRequirementCoverageRow` and `validators/validate-run.py` `_validate_requirement_deviations`.
119
+ - For `implementation-planning`, populate `implementationPlanning.requirementCoverage` with one row per concrete requirement from the brief / packet, using IDs `R-001`, `R-002`, ... in source order. A `covered` row's `coveredBy` MUST name the specific Option Candidate plus Stage/Step that satisfies the requirement. Use `status: "covered"` only when the report's plan actually covers it; use `documented-deviation` only when `coveredBy` states the concrete alternative and the row records non-empty unique `decisionRefs` plus `approvalDisposition`. Each `C-NNN` ref must name a clarification in this report; each `D-NNNN` ref must name a `decisionDrafts[].number`. `approvalDisposition: "accepted"` requires a referenced clarification with `status: answered|resolved` and non-empty `userInput`; `approvalDisposition: "blocked C-NNN"` requires that same-report clarification to be `status: open, blocks: approval`. Otherwise use `gap` or `blocked C-NNN` and ensure the corresponding `Clarification Items` row blocks approval. Do not collapse this into `ticketCoverage`; ticket coverage is not requirement coverage. **Enforced:** `schemas/final-report-v2.0.schema.json` `$defs.ImplementationRequirementCoverageRow` and `validators/validate-run.py` `_validate_requirement_deviations`.
120
120
  - For `implementation-planning`, each `requirementCoverage` row's `source` is a graded cell, not prose — free text like `"carry-in from requirements-discovery C-001"` is rejected. Write exactly one of: `brief:EB-001` / `brief:PB-001` / `brief:EO-001`, an end-state id the brief declares — when the brief pins ids, citing a heading instead is rejected, because every brief carries the same generic headings and a heading cannot say WHICH reporter line the requirement came from (only a brief authored before the end-state sections existed still takes the older `brief:<heading>` form, and there the heading must literally exist in it); `derived:R-NNN — <one-line reason>`, whose chain must terminate at a `brief:` or `contract:` row of the same table without cycling; or `contract:<rule>`, for artifacts okstra's own phase contract mandates, whose allowlist is exactly the two tokens `decision-record-step` (the §5.4 Decision Drafts materialization step) and `glossary-step` (the glossary proposal step) — any other rule name is rejected, so never invent one. (Maintainer SSOT for that allowlist: `scripts/okstra_ctl/scope_provenance.py` in the okstra repo.) A requirement you cannot source this way does not belong in the table: put it in `clarificationItems[]` with `Blocks=approval`. **Enforced:** `validators/validate-run.py` `_validate_requirement_provenance`. In the same table, anchor every stage number in `coveredBy` to a `Stage` / `Stages` word (`Stage 2`, `Stages 1-3`) — `_validate_stage_has_requirement` reads that cell as prose and fails the plan when a Stage Map stage is cited by no row.
121
121
  - For `implementation-planning`, also populate `implementationPlanning.decisionDrafts` (one row per decision meeting all three decision-record criteria; `[]` otherwise) and `implementationPlanning.skippedAdrCandidates` (evaluated-but-dropped adr-candidates; `[]` otherwise). The schema excerpt enumerates the row shape; the renderer emits §5.4 `### Decision Drafts`. When `decisionDrafts` is non-empty, the plan's stages MUST carry a stepwise step that creates `.okstra/decisions/<NNNN>-<slug>.md` (validate-run gates this).
122
- - For `implementation-planning`, populate `implementationPlanning.variationPointAnalysis` — a `hasMultipleImplementations` judgement synthesized from the analysis workers' output, not a field filled in last. When it is `true`, write one `points[]` row per varying behavior carrying `behavior`, the two or more `implementations` that serve it, `evidence` (a `path:line`, or the sibling task / stage that already implements that behavior), and an `extractionDecision` of `extract` / `interfaceKind` / `coveredBy` (the Stage Map stage that builds the interface) / `rationale`; when it is `false`, write a non-empty `noVariationRationale` and leave `points` empty (the two branches are mutually exclusive). Do NOT pass a boilerplate rationale — `false` is the cheaper field to fill, and a `false` declaration the brief or the sibling code in the workers' evidence contradicts is a `P-Var` DISAGREE, not a saving. Also populate `implementationPlanning.recommendedOption.testSeams`: one row per boundary a test injects at and replaces, each carrying `boundary` / `injectedAs` / `replacedInTest`. An empty list is a conscious "no seam needed" claim, never a default for a field nobody filled. The schema excerpt enumerates both row shapes — author against it. (Maintainer SSOT for these two rules: the `Required deliverable shape` bullet in `prompts/profiles/implementation-planning.md` in the okstra repo; that path is not resolvable here, so it is provenance, not a file to open.) **Enforced:** `schemas/final-report-v1.0.schema.json` `$defs.VariationPointAnalysis` / `$defs.VariationPoint` (the block is in `implementationPlanning.required`) plus `testSeams` in `$defs.RecommendedOption`'s `required`; `validators/validate-run.py` `_validate_variation_point_analysis` rejects a rationale-less `false`, a `false` carrying points, a `true` with no point, an `extract: true` decision leaving `interfaceKind` or `coveredBy` empty, and a hexagonal project extracting as anything but a port; and every point becomes a `P-Var-*` plan item judged in §5.5.9.
123
- - When the `Task Type` is `improvement-discovery`, populate `## 5.9 Improvement Candidates` with the 11-column schema enforced by `validators/validate_improvement_report.py`. The `Expected behavior after` cell states in one observable sentence what becomes different once the candidate is applied it seeds the downstream brief's `EB-NNN` / `EO-NNN`, and an empty cell fails the run. Source the row IDs (`I-NNN`), lens whitelist, and Source workers patterns from `scripts/okstra_ctl/improvement_lenses.py` do NOT introduce new lens names or worker prefixes. `improvement-discovery` is NOT in the data.json schema enum, so author its markdown directly (not via `okstra-render-final-report.py`). Immediately after writing the markdown, run (`Bash`): `okstra inject-report-index <markdown path> --report-language <en|ko>`. That adds the top-of-report Index plus `I-NNN` / `C-NNN` scroll anchors; the run validator fails the report when the Index anchor is absent.
122
+ - For `implementation-planning`, populate `implementationPlanning.variationPointAnalysis` — a `hasMultipleImplementations` judgement synthesized from the analysis workers' output, not a field filled in last. When it is `true`, write one `points[]` row per varying behavior carrying `behavior`, the two or more `implementations` that serve it, `evidence` (a `path:line`, or the sibling task / stage that already implements that behavior), and an `extractionDecision` of `extract` / `interfaceKind` / `coveredBy` (the Stage Map stage that builds the interface) / `rationale`; when it is `false`, write a non-empty `noVariationRationale` and leave `points` empty (the two branches are mutually exclusive). Do NOT pass a boilerplate rationale — `false` is the cheaper field to fill, and a `false` declaration the brief or the sibling code in the workers' evidence contradicts is a `P-Var` DISAGREE, not a saving. Also populate `implementationPlanning.recommendedOption.testSeams`: one row per boundary a test injects at and replaces, each carrying `boundary` / `injectedAs` / `replacedInTest`. An empty list is a conscious "no seam needed" claim, never a default for a field nobody filled. The schema excerpt enumerates both row shapes — author against it. (Maintainer SSOT for these two rules: the `Required deliverable shape` bullet in `prompts/profiles/implementation-planning.md` in the okstra repo; that path is not resolvable here, so it is provenance, not a file to open.) **Enforced:** `schemas/final-report-v2.0.schema.json` `$defs.VariationPointAnalysis` / `$defs.VariationPoint` (the block is in `implementationPlanning.required`) plus `testSeams` in `$defs.RecommendedOption`'s `required`; `validators/validate-run.py` `_validate_variation_point_analysis` rejects a rationale-less `false`, a `false` carrying points, a `true` with no point, an `extract: true` decision leaving `interfaceKind` or `coveredBy` empty, and a hexagonal project extracting as anything but a port; and every point becomes a `P-Var-*` plan item judged in §5.5.9.
123
+ - When the `Task Type` is `improvement-discovery`, populate `improvementDiscovery.candidates[]`, `improvementDiscovery.lensCoverage[]`, `improvementDiscovery.selectionLimit`, and `improvementDiscovery.userNarrative`. Each candidate carries the 11 logical fields enforced by `validators/validate_improvement_report.py`; each lens-coverage row records candidate IDs or an evidence-backed no-candidate rationale. Source IDs, lens names, and worker prefixes from `scripts/okstra_ctl/improvement_lenses.py`. The standard renderer derives the AI handoff Markdown; never author a free-form improvement report.
124
124
 
125
125
  Write the three completion artifacts and the separate audit sidecar with your `Write` tool — that is the canonical authoring path, and okstra ships no hook that blocks `.md` writes (its only settings hook is the `SessionEnd` trace-cleanup; the coding-preflight hook emits reminders but never blocks). A Bash heredoc is acceptable ONLY when a specific `Write` call is genuinely rejected by the host environment, and it MUST produce byte-identical content — do not reach for it pre-emptively. After writing data.json, invoke the renderer (`Bash`): `okstra render-final-report <data.json path>`, then write the Worker Result Path pointer. Confirm data.json, rendered Markdown, the pointer, and the audit sidecar exist before responding with a short status line prefixed by your model identity, per the preamble §"Return message to the lead". **Enforced:** dispatch `completionPaths` requires the first three files and `validators/validate_session_conformance.py` validates the audit sidecar.
126
126
 
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env python3
2
- """CLI entry for the final-report renderer.
2
+ """CLI entry for the schema-selected AI handoff Markdown renderer.
3
3
 
4
4
  Usage:
5
5
  python3 scripts/okstra-render-final-report.py \\
@@ -57,9 +57,9 @@ def main(argv: list[str]) -> int:
57
57
  type=Path,
58
58
  default=None,
59
59
  help=(
60
- "Optional override for the Jinja2 template file. Default: "
61
- "$OKSTRA_HOME/templates/reports/final-report.template.md or "
62
- "the repo-local copy."
60
+ "Optional override for the Jinja2 template file. By default, "
61
+ "data.json.schemaVersion selects the matching installed or "
62
+ "repo-local report template."
63
63
  ),
64
64
  )
65
65
  parser.add_argument(