okstra 0.179.2 → 0.183.2

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 (258) hide show
  1. package/README.md +1 -1
  2. package/dist/cli-registry.mjs +30 -2
  3. package/dist/cli-registry.mjs.map +1 -1
  4. package/dist/commands/execute/incremental-carry.mjs +9 -8
  5. package/dist/commands/execute/incremental-carry.mjs.map +1 -1
  6. package/dist/commands/execute/plan-verify.mjs +3 -1
  7. package/dist/commands/execute/plan-verify.mjs.map +1 -1
  8. package/dist/commands/execute/render-bundle.d.mts +4 -2
  9. package/dist/commands/execute/render-bundle.mjs +46 -5
  10. package/dist/commands/execute/render-bundle.mjs.map +1 -1
  11. package/dist/commands/execute/run.mjs +11 -3
  12. package/dist/commands/execute/run.mjs.map +1 -1
  13. package/dist/commands/inspect/model-io.d.mts +1 -0
  14. package/dist/commands/inspect/model-io.mjs +25 -0
  15. package/dist/commands/inspect/model-io.mjs.map +1 -0
  16. package/dist/commands/inspect/stage-map.mjs +29 -8
  17. package/dist/commands/inspect/stage-map.mjs.map +1 -1
  18. package/dist/commands/inspect/task-list.mjs +52 -6
  19. package/dist/commands/inspect/task-list.mjs.map +1 -1
  20. package/dist/commands/inspect/user-response.mjs +14 -4
  21. package/dist/commands/inspect/user-response.mjs.map +1 -1
  22. package/dist/commands/lifecycle/check-project.d.mts +1 -0
  23. package/dist/commands/lifecycle/check-project.mjs +69 -50
  24. package/dist/commands/lifecycle/check-project.mjs.map +1 -1
  25. package/dist/commands/lifecycle/contract-check.d.mts +1 -0
  26. package/dist/commands/lifecycle/contract-check.mjs +18 -0
  27. package/dist/commands/lifecycle/contract-check.mjs.map +1 -0
  28. package/dist/commands/lifecycle/preflight.mjs +154 -51
  29. package/dist/commands/lifecycle/preflight.mjs.map +1 -1
  30. package/dist/commands/pr/pr.d.mts +1 -0
  31. package/dist/commands/pr/pr.mjs +19 -1
  32. package/dist/commands/pr/pr.mjs.map +1 -1
  33. package/dist/commands/report/agent-activity.mjs +2 -2
  34. package/dist/commands/report/approval-decision.d.mts +1 -0
  35. package/dist/commands/report/approval-decision.mjs +21 -0
  36. package/dist/commands/report/approval-decision.mjs.map +1 -0
  37. package/dist/commands/report/design-snapshot.d.mts +1 -0
  38. package/dist/commands/report/design-snapshot.mjs +19 -0
  39. package/dist/commands/report/design-snapshot.mjs.map +1 -0
  40. package/dist/commands/report/translate.mjs +3 -0
  41. package/dist/commands/report/translate.mjs.map +1 -1
  42. package/dist/lib/host-registry-client.mjs +13 -9
  43. package/dist/lib/host-registry-client.mjs.map +1 -1
  44. package/docs/architecture/storage-model.md +1 -1
  45. package/docs/architecture.md +21 -10
  46. package/docs/cli.md +36 -20
  47. package/docs/container.md +6 -4
  48. package/docs/contributor-change-matrix.md +1 -1
  49. package/docs/for-ai/README.md +2 -2
  50. package/docs/for-ai/skills/okstra-brief-gen.md +5 -3
  51. package/docs/for-ai/skills/okstra-code-review.md +4 -4
  52. package/docs/for-ai/skills/okstra-container-build.md +20 -17
  53. package/docs/for-ai/skills/okstra-inspect.md +20 -23
  54. package/docs/for-ai/skills/okstra-manager.md +19 -18
  55. package/docs/for-ai/skills/okstra-memory.md +2 -2
  56. package/docs/for-ai/skills/okstra-pr-gen.md +3 -3
  57. package/docs/for-ai/skills/okstra-rollup.md +14 -13
  58. package/docs/for-ai/skills/okstra-run.md +7 -3
  59. package/docs/for-ai/skills/okstra-schedule-gen.md +15 -18
  60. package/docs/for-ai/skills/okstra-setup.md +7 -7
  61. package/docs/for-ai/skills/okstra-usage.md +5 -4
  62. package/docs/for-ai/skills/okstra-user-response.md +50 -32
  63. package/docs/project-structure-overview.md +43 -31
  64. package/docs/task-process/implementation-planning.md +2 -2
  65. package/docs/task-process/implementation.md +7 -4
  66. package/package.json +1 -1
  67. package/runtime/BUILD.json +2 -2
  68. package/runtime/agents/workers/claude-worker.md +4 -4
  69. package/runtime/agents/workers/report-writer-worker.md +15 -164
  70. package/runtime/agents/workers/translator-worker.md +5 -13
  71. package/runtime/bin/okstra-error-log.py +51 -11
  72. package/runtime/bin/okstra-report-translate.py +210 -23
  73. package/runtime/prompts/host-orchestration/implementation.md +1 -1
  74. package/runtime/prompts/launch.template.md +9 -12
  75. package/runtime/prompts/lead/adapters/cmux.md +1 -1
  76. package/runtime/prompts/lead/context-loader.md +41 -141
  77. package/runtime/prompts/lead/convergence.md +10 -8
  78. package/runtime/prompts/lead/okstra-lead-contract.md +40 -49
  79. package/runtime/prompts/lead/plan-body-verification.md +217 -23
  80. package/runtime/prompts/lead/report-writer.md +81 -421
  81. package/runtime/prompts/lead/team-contract.md +9 -54
  82. package/runtime/prompts/profiles/_clarification-recommendation.md +5 -4
  83. package/runtime/prompts/profiles/_coding-conventions-preflight.md +3 -2
  84. package/runtime/prompts/profiles/_common-contract.md +4 -4
  85. package/runtime/prompts/profiles/_implementation-deliverable.md +1 -1
  86. package/runtime/prompts/profiles/_implementation-diff-review.md +1 -1
  87. package/runtime/prompts/profiles/_implementation-verifier.md +3 -3
  88. package/runtime/prompts/profiles/change-impact-analysis.md +1 -1
  89. package/runtime/prompts/profiles/error-analysis.md +1 -1
  90. package/runtime/prompts/profiles/feature-analysis.md +1 -1
  91. package/runtime/prompts/profiles/final-verification.md +1 -1
  92. package/runtime/prompts/profiles/implementation-planning.md +20 -12
  93. package/runtime/prompts/profiles/improvement-discovery.md +1 -1
  94. package/runtime/prompts/profiles/project-analysis.md +1 -1
  95. package/runtime/prompts/profiles/release-handoff.md +0 -1
  96. package/runtime/prompts/profiles/requirements-discovery.md +1 -1
  97. package/runtime/prompts/wizard/prompts.ko.json +0 -1
  98. package/runtime/python/okstra_ctl/adapters/hosts/capability_adapter.py +69 -17
  99. package/runtime/python/okstra_ctl/adapters/hosts/claude-code/adapter.py +13 -4
  100. package/runtime/python/okstra_ctl/adapters/hosts/claude-code/relay.md +8 -2
  101. package/runtime/python/okstra_ctl/adapters/hosts/codex/adapter.py +2 -2
  102. package/runtime/python/okstra_ctl/adapters/hosts/codex/relay.md +50 -5
  103. package/runtime/python/okstra_ctl/adapters/hosts/external/relay.md +1 -1
  104. package/runtime/python/okstra_ctl/adapters/hosts/grok/adapter.py +2 -2
  105. package/runtime/python/okstra_ctl/adapters/hosts/grok/relay.md +66 -5
  106. package/runtime/python/okstra_ctl/adapters/providers/grok/adapter.py +70 -2
  107. package/runtime/python/okstra_ctl/agent_activity.py +141 -38
  108. package/runtime/python/okstra_ctl/agent_invocation.py +19 -6
  109. package/runtime/python/okstra_ctl/agent_prompt_cli.py +70 -23
  110. package/runtime/python/okstra_ctl/analysis_inputs.py +5 -4
  111. package/runtime/python/okstra_ctl/analysis_packet.py +124 -3
  112. package/runtime/python/okstra_ctl/approval_decisions.py +328 -0
  113. package/runtime/python/okstra_ctl/attempt_evidence.py +2 -2
  114. package/runtime/python/okstra_ctl/backfill.py +13 -10
  115. package/runtime/python/okstra_ctl/batch.py +2 -4
  116. package/runtime/python/okstra_ctl/build_tools.py +6 -3
  117. package/runtime/python/okstra_ctl/claim_reproduction.py +101 -0
  118. package/runtime/python/okstra_ctl/clarification_items.py +27 -13
  119. package/runtime/python/okstra_ctl/cmux.py +130 -52
  120. package/runtime/python/okstra_ctl/code_review_target.py +34 -8
  121. package/runtime/python/okstra_ctl/consumers.py +5 -4
  122. package/runtime/python/okstra_ctl/container.py +103 -8
  123. package/runtime/python/okstra_ctl/context_cost.py +2 -1
  124. package/runtime/python/okstra_ctl/contract_graph.py +497 -0
  125. package/runtime/python/okstra_ctl/contract_graph_cli.py +62 -0
  126. package/runtime/python/okstra_ctl/convergence.py +338 -17
  127. package/runtime/python/okstra_ctl/convergence_engine.py +10 -18
  128. package/runtime/python/okstra_ctl/convergence_provenance.py +58 -8
  129. package/runtime/python/okstra_ctl/convergence_store.py +55 -34
  130. package/runtime/python/okstra_ctl/design_prep.py +7 -4
  131. package/runtime/python/okstra_ctl/design_snapshot.py +134 -0
  132. package/runtime/python/okstra_ctl/dispatch_core.py +97 -69
  133. package/runtime/python/okstra_ctl/dispatch_state.py +163 -63
  134. package/runtime/python/okstra_ctl/doctor.py +6 -3
  135. package/runtime/python/okstra_ctl/domain/worker_presentation.py +70 -9
  136. package/runtime/python/okstra_ctl/entrypoints/hosts.py +16 -30
  137. package/runtime/python/okstra_ctl/error_log_write.py +35 -30
  138. package/runtime/python/okstra_ctl/error_report.py +26 -1
  139. package/runtime/python/okstra_ctl/error_zip.py +27 -5
  140. package/runtime/python/okstra_ctl/execution_identity.py +3 -2
  141. package/runtime/python/okstra_ctl/execution_manifest.py +7 -4
  142. package/runtime/python/okstra_ctl/execution_mutation_audit.py +6 -2
  143. package/runtime/python/okstra_ctl/final_report_schema.py +26 -17
  144. package/runtime/python/okstra_ctl/fix_cycles.py +2 -2
  145. package/runtime/python/okstra_ctl/fixed_text.py +39 -0
  146. package/runtime/python/okstra_ctl/git_reconcile.py +41 -9
  147. package/runtime/python/okstra_ctl/handoff.py +5 -4
  148. package/runtime/python/okstra_ctl/i18n.py +4 -2
  149. package/runtime/python/okstra_ctl/implementation_direction.py +22 -14
  150. package/runtime/python/okstra_ctl/implementation_outcome.py +4 -7
  151. package/runtime/python/okstra_ctl/incremental_carry.py +130 -17
  152. package/runtime/python/okstra_ctl/incremental_scope.py +12 -4
  153. package/runtime/python/okstra_ctl/index.py +8 -11
  154. package/runtime/python/okstra_ctl/initial_prompt_materialization.py +79 -7
  155. package/runtime/python/okstra_ctl/invocation.py +3 -6
  156. package/runtime/python/okstra_ctl/json_boundary.py +366 -0
  157. package/runtime/python/okstra_ctl/json_registry.py +10 -12
  158. package/runtime/python/okstra_ctl/jsonl.py +19 -2
  159. package/runtime/python/okstra_ctl/lead_events.py +33 -1
  160. package/runtime/python/okstra_ctl/listing.py +3 -3
  161. package/runtime/python/okstra_ctl/log_report.py +24 -2
  162. package/runtime/python/okstra_ctl/manager_cli.py +92 -7
  163. package/runtime/python/okstra_ctl/manager_store.py +12 -10
  164. package/runtime/python/okstra_ctl/material.py +5 -1
  165. package/runtime/python/okstra_ctl/migrate.py +29 -25
  166. package/runtime/python/okstra_ctl/model_cli.py +3 -15
  167. package/runtime/python/okstra_ctl/model_io_cli.py +1051 -0
  168. package/runtime/python/okstra_ctl/mutation_probe.py +13 -4
  169. package/runtime/python/okstra_ctl/pane_reclaim.py +3 -2
  170. package/runtime/python/okstra_ctl/path_hints.py +12 -0
  171. package/runtime/python/okstra_ctl/paths.py +21 -0
  172. package/runtime/python/okstra_ctl/plan_items.py +115 -4
  173. package/runtime/python/okstra_ctl/plan_items_cli.py +626 -34
  174. package/runtime/python/okstra_ctl/ports/worker_dispatch.py +2 -1
  175. package/runtime/python/okstra_ctl/pr_template.py +3 -2
  176. package/runtime/python/okstra_ctl/project_meta.py +5 -7
  177. package/runtime/python/okstra_ctl/recap.py +5 -4
  178. package/runtime/python/okstra_ctl/reconcile.py +21 -27
  179. package/runtime/python/okstra_ctl/registry/host_discovery.py +3 -2
  180. package/runtime/python/okstra_ctl/registry/provider_registry.py +3 -2
  181. package/runtime/python/okstra_ctl/render.py +74 -16
  182. package/runtime/python/okstra_ctl/render_final_report.py +10 -8
  183. package/runtime/python/okstra_ctl/report_assembly.py +509 -0
  184. package/runtime/python/okstra_ctl/report_contract.py +2 -1
  185. package/runtime/python/okstra_ctl/report_finalize.py +67 -32
  186. package/runtime/python/okstra_ctl/report_html/render.py +3 -2
  187. package/runtime/python/okstra_ctl/report_inputs.py +72 -0
  188. package/runtime/python/okstra_ctl/report_language.py +3 -2
  189. package/runtime/python/okstra_ctl/report_markdown.py +81 -8
  190. package/runtime/python/okstra_ctl/report_narrative.py +351 -0
  191. package/runtime/python/okstra_ctl/report_projections.py +265 -0
  192. package/runtime/python/okstra_ctl/report_synthesis_packet.py +518 -0
  193. package/runtime/python/okstra_ctl/report_views.py +3 -2
  194. package/runtime/python/okstra_ctl/rollup.py +65 -4
  195. package/runtime/python/okstra_ctl/run.py +184 -65
  196. package/runtime/python/okstra_ctl/run_audit.py +3 -2
  197. package/runtime/python/okstra_ctl/run_context.py +6 -9
  198. package/runtime/python/okstra_ctl/run_index_row.py +2 -8
  199. package/runtime/python/okstra_ctl/schedule_semantics.py +5 -2
  200. package/runtime/python/okstra_ctl/schema_excerpt.py +15 -8
  201. package/runtime/python/okstra_ctl/session_transcript.py +27 -1
  202. package/runtime/python/okstra_ctl/set_work_status.py +64 -38
  203. package/runtime/python/okstra_ctl/stage_fix_carry.py +8 -6
  204. package/runtime/python/okstra_ctl/stage_ledger.py +132 -18
  205. package/runtime/python/okstra_ctl/stage_map.py +96 -28
  206. package/runtime/python/okstra_ctl/stage_targets.py +3 -4
  207. package/runtime/python/okstra_ctl/team.py +3 -2
  208. package/runtime/python/okstra_ctl/team_reconcile.py +11 -2
  209. package/runtime/python/okstra_ctl/time_report.py +51 -4
  210. package/runtime/python/okstra_ctl/usage_identity.py +2 -1
  211. package/runtime/python/okstra_ctl/usage_report.py +58 -4
  212. package/runtime/python/okstra_ctl/user_response.py +1431 -66
  213. package/runtime/python/okstra_ctl/wizard.py +11 -44
  214. package/runtime/python/okstra_ctl/work_categories.py +3 -2
  215. package/runtime/python/okstra_ctl/worker_dispatch.py +5 -2
  216. package/runtime/python/okstra_ctl/worker_prompt_body.py +49 -3
  217. package/runtime/python/okstra_ctl/worker_prompt_contract.py +3 -2
  218. package/runtime/python/okstra_ctl/worker_prompt_policy.py +31 -3
  219. package/runtime/python/okstra_ctl/worker_runner.py +14 -12
  220. package/runtime/python/okstra_ctl/workflow.py +2 -1
  221. package/runtime/python/okstra_ctl/worktree.py +3 -2
  222. package/runtime/python/okstra_ctl/wrapper_status.py +4 -2
  223. package/runtime/python/okstra_ctl/write_policy.py +4 -2
  224. package/runtime/python/okstra_token_usage/antigravity.py +39 -12
  225. package/runtime/python/okstra_token_usage/collect.py +90 -38
  226. package/runtime/python/okstra_token_usage/grok.py +127 -0
  227. package/runtime/schemas/final-report-v2.0.schema.json +9 -0
  228. package/runtime/schemas/final-report-v3.0.schema.json +10219 -0
  229. package/runtime/schemas/report-narrative-v3.0.schema.json +30 -0
  230. package/runtime/schemas/report-synthesis-packet-v1.0.schema.json +140 -0
  231. package/runtime/skills/okstra-brief-gen/SKILL.md +9 -7
  232. package/runtime/skills/okstra-code-review/SKILL.md +21 -11
  233. package/runtime/skills/okstra-container-build/SKILL.md +18 -18
  234. package/runtime/skills/okstra-inspect/SKILL.md +12 -11
  235. package/runtime/skills/okstra-inspect/facets/error-zip.md +8 -8
  236. package/runtime/skills/okstra-inspect/facets/errors.md +2 -2
  237. package/runtime/skills/okstra-inspect/facets/history.md +9 -14
  238. package/runtime/skills/okstra-inspect/facets/logs.md +2 -2
  239. package/runtime/skills/okstra-inspect/facets/recap.md +5 -5
  240. package/runtime/skills/okstra-inspect/facets/report.md +6 -10
  241. package/runtime/skills/okstra-inspect/facets/status.md +9 -8
  242. package/runtime/skills/okstra-inspect/facets/time.md +3 -3
  243. package/runtime/skills/okstra-manager/SKILL.md +16 -14
  244. package/runtime/skills/okstra-memory/SKILL.md +3 -3
  245. package/runtime/skills/okstra-pr-gen/SKILL.md +5 -4
  246. package/runtime/skills/okstra-rollup/SKILL.md +6 -16
  247. package/runtime/skills/okstra-run/SKILL.md +7 -7
  248. package/runtime/skills/okstra-schedule-gen/SKILL.md +21 -17
  249. package/runtime/skills/okstra-setup/SKILL.md +21 -13
  250. package/runtime/skills/okstra-setup/references/project-config.md +2 -2
  251. package/runtime/skills/okstra-usage/SKILL.md +10 -10
  252. package/runtime/skills/okstra-user-response/SKILL.md +78 -107
  253. package/runtime/templates/report-writer-prompt-preamble.md +29 -19
  254. package/runtime/templates/reports/html/macros/forms.html +6 -4
  255. package/runtime/templates/reports/schedule.template.md +4 -4
  256. package/runtime/templates/worker-error-contract.md +17 -29
  257. package/runtime/validators/validate-run.py +636 -72
  258. package/runtime/validators/validate_session_conformance.py +43 -7
@@ -1,459 +1,119 @@
1
- # Okstra Report Writer Contract
1
+ # Report Writer and Report Assembly Contract
2
2
 
3
- ## File-author ownership (BLOCKING)
3
+ ## Phase 6 ownership
4
4
 
5
- The final-report data.json is authored by `Report writer worker` when that role is in the roster. The lead reviews the human HTML and the report record but does not write them. Lead-authored fallback is legal only after a real `dispatch_worker` attempt records `error`, `timeout`, or `not-run` with a concrete reason. `release-handoff` remains the intentional single-lead exception.
5
+ Phase 6 gives the report writer all settled analysis, convergence, task, and planning context required for synthesis. Read access supplies context; it does not grant write authority over another owner's file.
6
6
 
7
- The JSON SSOT path is `runs/<task-type>/reports/final-report-<task-type>-<seq>.data.json`. Phase 7 produces the task-specific human HTML sibling from that record. The full reading copy is rendered on demand with `okstra render-final-report <data.json>`. The worker-result pointer at `**Worker Result Path:**` records the record path plus reconciled convergence input. Completion artifacts are the record, the pointer, and the audit sidecar; HTML follows during finalization.
7
+ The report writer writes only the report narrative Markdown, its pointer record, and its audit sidecar. It must not write or patch `final-report-*.data.json`, the approval decision ledger, activity ledger, team state, convergence state, or design-preparation input.
8
8
 
9
- New bundles use `schemas/final-report-v2.0.schema.json`. The Markdown keeps verdict, routing, evidence, one structured task deliverable, and audit data for the next agent. The HTML uses `humanSummary`, task `userNarrative`, and structured facts for the user. Raw worker discussion, convergence mechanics, and usage belong to audit structures and never to the HTML human main body.
9
+ The narrative owns judgments, plan body, summaries, user explanation, recommended steps, and follow-up descriptions. It never owns `designPreparation`, `designSurfaceCoverage`, `executionStatus`, `executionRoles`, `tokenUsage`, `crossVerification`, `approvalContext`, `clarificationItems`, `agentActivity`, or `planBodyVerification`. It must not pre-fill a future round, future gate result, activity identifier, usage value, or resolution.
10
10
 
11
- ### Implementation-planning frontmatter contract
11
+ Report assembly reads the role-owned inputs, validates them, derives links and statuses, and publishes the final record once. An error identifies `owner`, `artifactPath`, `fieldPath`, and `reason`; return it only to the named owner.
12
12
 
13
- #### Selected-direction
13
+ ## Role-owned inputs
14
14
 
15
- Emit `frontmatter.approved` as `false` and copy `implementationPlanning.selectedDirectionRef.snapshotPath` into `frontmatter.selectedDirectionRef`. You MUST omit `frontmatter.implementationOption`; the direction was selected upstream and cannot be selected again in planning. `schemas/final-report-v2.0.schema.json` enforces the required selected-direction reference and rejects an `implementationOption` property for this branch.
15
+ | Input | Owner | Write boundary |
16
+ |---|---|---|
17
+ | report narrative Markdown | report-writer | `reportNarrativePath` |
18
+ | approval decision ledger | lead | `approvalDecisionsPath` through `okstra approval-decision` |
19
+ | activity ledger | activity recorder | `leadEventsPath` through `okstra agent-activity append` |
20
+ | team state and usage | runtime/accounting adapter | `teamStatePath` |
21
+ | convergence state and plan-body result | convergence engine | `convergenceStatePath` |
22
+ | design-preparation snapshot | design-surface detector | `designPreparationPath` |
23
+ | final report record | report assembly | `expectedReportRecordPath` |
16
24
 
17
- #### Legacy candidate-comparison
25
+ An active clarification exists only in `activeClarifications[]`. A decision carried from a previous run exists only in `carriedDecisions[]`; do not recreate it as an active question.
18
26
 
19
- Emit `frontmatter.approved` as `false` and `frontmatter.implementationOption` as the empty string `""`. The user later flips `approved` to `true` and fills `implementationOption` with the chosen Option Candidate name to authorise and scope the next `implementation` run. Every other report type follows the same empty `implementationOption` default; the schema's non-selected-direction branch requires that field and rejects a selected-direction reference.
27
+ Each decision option has `role`, `answer`, `rationale`, `disposition`, `reach`, optional `scopeEffects`, `addedWork`, and `directionChange`. `reach` is exactly one of `in-repo` or `cross-repo`. `scopeEffects` may contain `new-schema` and `deferrable`. A `correctness-critical` option cannot use `select` or `accept-risk`; a `noncritical-dissent` option cannot use `select`.
20
28
 
21
- **As the report-writer worker:** YOU write the report record; the file on disk is the canonical record, so do not return it inline. Do not invoke `okstra render-final-report`.
29
+ Resolution `checkRefs` name existing `A-NNN` activity rows. Those activity rows carry `clarificationRefs[]`; their `planItemIds[]` let report assembly derive the reverse plan-item links. Do not store copied plan or activity identifiers in `approvalContext`.
22
30
 
23
- **As the lead:** prepare the report-writer prompt, dispatch the Report writer worker per the Phase 6 dispatch template in okstra-lead-contract.md, and review the record, the pointer, and the separate audit sidecar in Phase 7. Do not call `write_artifact` against the report paths or worker-result pointer yourself when Report writer worker is in the roster.
31
+ ## Report-writer dispatch
24
32
 
25
- ## When to Use
33
+ For report contract 3.0, prompt materialization first freezes one report synthesis
34
+ packet. The packet contains the task brief, analysis packet, report template,
35
+ report schema, convergence state, every successful settled worker result recorded
36
+ by the run manifest, accumulated `user-responses/` sidecars, and the current
37
+ session/token/cost accounting snapshot. Each file-backed source carries its owner,
38
+ project-relative path, SHA-256 digest, and value. The report writer receives the
39
+ packet's Markdown reading projection as the only task input instead of an
40
+ independently assembled list of raw paths.
26
41
 
27
- - During okstra lead Phase 6 (final report assembly)
28
- - During okstra lead Phase 7 (artifact persistence — verification only when Report writer worker is in roster)
29
- - When verifying the structure of the final report
42
+ The packet's authoring contract names the result path, narrative format, writing
43
+ instructions, runtime-owned content, and validation rules. Missing configured
44
+ paths and missing files are collected across the full source set before dispatch
45
+ and reported together with their owners. Report assembly compares every frozen
46
+ source digest again and reports every changed or missing source in one result.
47
+ This is enforced by
48
+ `initial_prompt_materialization._materialize_report_writer_packet()`,
49
+ `report_synthesis_packet.build_report_synthesis_packet()`, and
50
+ `report_assembly.assemble_report()`.
30
51
 
31
- ## Phase 6 dispatch template (Report writer worker)
52
+ Materialize the duty prompt with `okstra agent-prompt materialize --audience report-writer`. The prompt starts with these anchors in order:
32
53
 
33
- 1. Resolve the Report writer worker assignment and all required prompt/result/error paths from the manifests.
34
- 2. Write a call-specific task-instructions file containing the anchor headers and audience-specific reading list.
35
- 3. Run `okstra agent-prompt materialize --audience report-writer --assignment-ref initial/report-writer --worker-id report-writer --dispatch-kind report-writer ...`, then run `okstra agent-prompt verify` against the returned `metadataPath`. Use the returned `promptPath` without appending role prose. A correction redispatch repeats this step with a fresh invocation ID and the same audience and assignment reference.
36
- 4. Emit the Phase 6 checkpoint.
37
- 5. For `runner=native-session`, first run `okstra agent-prompt record-dispatch` with the project root, run manifest, metadata path, and `--enforcement-mode host-native-spec-link-gate`, then call the host primitive with only the returned `hostModelValue`. After its result exists, run `okstra agent-prompt link-result` with `--dispatch-id <invocationId>:attempt-1` and the result path before accepting it. For `runner=cli-wrapper`, call `okstra team dispatch` when `terminalBackend` is `cmux-pane`, or `okstra worker-dispatch --workers report-writer` otherwise, which consumes `modelExecutionValue` and verifies the metadata before starting the provider process. Never combine this Phase 6 call with analysis workers.
38
- 6. Call `await_workers([handle])` and verify the data.json Result Path and worker-result pointer at Worker Result Path. Verify the separate heartbeat audit sidecar before accepting the run. **Enforced:** both dispatch adapters keep the two completion paths in `WorkerJob.completion_paths`, and `validators/validate_session_conformance.py` validates the audit sidecar.
54
+ 1. `**Project Root:**`
55
+ 2. `**Prompt History Path:**`
56
+ 3. `**Result Path:**`
57
+ 4. `**Worker Result Path:**`
58
+ 5. `**Worker Preamble Path:**`
59
+ 6. `**Worker Error Contract Path:**`
60
+ 7. `**Errors log path:**`
61
+ 8. `**Errors sidecar path:**`
39
62
 
40
- The complete assignment supplies both runner-specific model values and the prompt header in item 9 below. A native host uses `hostModelValue`; a deterministic provider process uses `modelExecutionValue`; the recorded `**Model:**` header remains the canonical assignment label. Missing or unsupported model resolution is a pre-dispatch contract failure; the common contract does not choose a runtime fallback.
63
+ The errors sidecar anchor reserves the runtime-owned write-artifact path used by dispatch validation. No model-authored error JSON file is part of report-writer dispatch; failures use the typed error-log command from the worker error contract.
41
64
 
42
- The prompt MUST include, in this order at the top:
65
+ Register the dispatch with `okstra agent-prompt record-dispatch`. When `terminalBackend` is `cmux-pane`, run `okstra team dispatch`; for `runner: cli-wrapper`, run `okstra worker-dispatch --audience report-writer`. Attach the result through `okstra agent-prompt link-result`. The result path is the report narrative Markdown, not the final record.
43
66
 
44
- 1. `**Project Root:** <absolute-path>`
45
- 2. `**Prompt History Path:** <project-relative-path>` (under current run `prompts/`)
46
- 3. `**Result Path:** runs/<task-type>/reports/final-report-<task-type>-<seq>.data.json` — canonical JSON SSOT. The full reading copy is rendered on demand.
47
- 4. `**Worker Result Path:** runs/<task-type>/worker-results/report-writer-worker-<task-type>-<seq>.md` — canonical three-path worker-result pointer and source for the audit-path derivation.
48
- 5. `**Audit sidecar path:** <absolute-path>` — the generated report-writer heartbeat/read-confirmation destination derived from the Markdown `**Worker Result Path:**`, never from Result Path.
49
- 6. `Assigned worker prompt history path: <absolute-path>`
50
- 7. The four BLOCKING dispatch anchor headers generated from the report-writer audience (the worker cannot synthesize any of these paths):
51
- - `**Worker Preamble Path:** <absolute-path>` — selects `templates/report-writer-prompt-preamble.md`.
52
- - `**Worker Error Contract Path:** <absolute-path>` — selects `templates/worker-error-contract.md`.
53
- - `**Errors log path:** <absolute-path>` — run-level errors JSONL (`logs/errors-<task-type>-<seq>.jsonl`).
54
- - `**Errors sidecar path:** <absolute-path>` — this worker's per-run sidecar JSON (`worker-results/report-writer-worker-errors-<task-type>-<seq>.json`).
55
- 8. `**Prompt Delivery Mode:** <mode>` — the selected adapter's declared `initialPromptDeliveryMode`.
56
- 9. `**Model:** Report writer worker, <modelExecutionValue>` (resolved per Phase 5.5 anchor-header rules)
57
- 10. The full `[Required reading]` clause (see [team-contract](./team-contract.md)) — for Phase 6 it adds two **per-task-type, instruction-set-local** read-only files, both scoped to this run's task-type by `okstra-ctl` at prep time:
58
- - `<instruction-set>/final-report-schema.json` — a task-type excerpt of schema v2. This is the binding authoring shape; the installed full schema is what the run is judged against. Do **NOT** pull the full repository schema because it is outside the task bundle.
59
- - `<instruction-set>/final-report-template.md` — the full reading copy template. It shows the agent-facing ledger shape, not the human presentation. The task-specific HTML renderer reads data.json separately.
60
- 11. The analysis packet path plus a one-line MCP pointer instead of copying the server block verbatim: `**MCP servers:** follow the analysis packet's "Available MCP Servers" section. If the section is absent or says none, treat MCP as unavailable for this run; never infer tools from host configuration.`
61
- 12. `Convergence state: runs/<task-type>/state/convergence-<task-type>-<seq>.json`, followed by pointers to all analysis-worker result files under `worker-results/`. The convergence path is deterministic and is listed even before Phase 5.5 creates the file. Read its classifications (Full/Partial/Contested/Worker-Unique), `roundHistory[]`, `round2SkippedReason`, and `finalClassificationCounts`; populate `crossVerification.roundHistory` in data.json so Section 6 can show which rounds executed, queue sizes, and why Round 2 was (or was not) skipped. The renderer prints the full per-round table only when more than one round ran; single-round or zero-round histories are auto-collapsed to a one-line summary.
62
- 13. `**Report Language:** <en|ko>` — must be either `en` or `ko`; `auto`
63
- has been resolved by the lead from project.json / global config
64
- before the dispatch is constructed. The worker copies this verbatim
65
- into `data.json.meta.reportLanguage`.
66
- 14. An explicit instruction: `You are the author of TWO files: (a) the report record at <Result Path>, and (b) the worker-result pointer at <Worker Result Path>. Maintain the separate heartbeat audit sidecar at <Audit sidecar path>. Do not return the report inline. Do not invoke okstra render-final-report. The dispatch fails when either completion artifact is missing, and session conformance fails when the audit sidecar is missing or invalid.`
67
- 15. The prose budget (dedup contract): `verdictCard.finalConclusion` is the conclusion SSOT — at most 3 sentences. `rationale.*` fields stay within 2 sentences each; `humanSummary` entries stay concise; task `userNarrative` explains each user-facing section once with evidence references. Do not copy these narratives into the AI Markdown. `summary` stays at 3-5 rows unless the run covers multiple tickets. Generation time scales with output volume, so exceeding the budget is a cost bug, not extra diligence.
67
+ The pointer record contains the narrative and audit paths. Completion never depends on the final record because assembly runs after writer completion.
68
68
 
69
- **Fix-run incremental authoring (applies when the run's profile carries a "Fix-Run Carry" block).** Do not author the data.json from scratch. Start by copying the previous run's data.json (the `Previous report` path in the Fix-Run Carry block) to this run's Result Path, then update ONLY the blocks the fix run changed: `meta`/`header` (run seq, dates), `executionStatus`, `implementation.verifierResults`, `implementation.validationEvidence`, `implementation.commitList` / `diffSummary`, `crossVerification`, `verdictCard`, `finalVerdict`, and any `evidence` rows the fix touched. Deliverable prose for unchanged sections is carried forward verbatim — do not re-generate it. Do not invoke the reading-copy renderer. The schema validation contract is unchanged, so an incrementally-authored data.json passes the same post-hoc gates. The lead's dispatch prompt MUST include the previous data.json path when the carry block is present.
69
+ ## Implementation-planning sequence
70
70
 
71
- **Completion detection after dispatch (BLOCKING).** A dispatch acknowledgement is NOT completion — detect completion via the SSOT protocol in [team-contract](./team-contract.md) "Worker-completion detection", with a pending set covering the data.json (Result Path) and worker-result pointer (Worker Result Path). Check the separate audit sidecar before accepting conformance. Do NOT end the turn with a prose "waiting for the report" statement. **Enforced:** the adapters reject a completed transition while any `completionPaths` entry is absent; `validators/validate_session_conformance.py` owns the audit check.
71
+ 1. Dispatch the report writer and wait for the narrative and pointer.
72
+ 2. Parse the narrative and extract the deterministic plan-item queue without publishing `data.json`.
73
+ 3. Run initial plan-body verification as round 1.
74
+ 4. Apply at most one automatic planner self-fix to the narrative.
75
+ 5. Run targeted re-verification as round 2 when needed.
76
+ 6. Persist the completed `planBodyVerification` value in convergence state.
77
+ 7. Complete the design-surface detector snapshot.
78
+ 8. Run Phase 7 report assembly.
72
79
 
73
- ### Resume-safe dispatch
80
+ The writer never predicts steps 3–7 in its draft. The plan-body verifier reads the plan body, and report assembly adds its completed result afterward.
74
81
 
75
- A resumed lead can always call `redispatch_worker` for a fresh Report writer worker. Resume/session mechanics are adapter-owned and are never a reason to bypass the role.
82
+ **Implementation-planning direction branch.** A selected-direction narrative carries `selectedDirectionRef` and `directionRealization`; the `P-Dir-1` verifier checks that realization against the selected core mechanism, architecture boundaries, planning invariants, and any hidden direction change. A legacy candidate-comparison narrative retains `P-Opt-*` option comparison semantics. These are plan-body judgments, not frontmatter fields owned by the writer.
76
83
 
77
- ### Lead-authored fallback (only if dispatch failed)
84
+ **Implementation-option-selection comparison.** Candidate details remain direction-level and must not claim planning precision:
78
85
 
79
- Except for `release-handoff` (which is single-lead by design and never dispatches a Report writer worker — see "Release-handoff section contract" below), lead-authored fallback is permitted only if all of the following are true and recorded in team-state:
80
-
81
- 1. A Report writer worker dispatch was actually attempted through `dispatch_worker`.
82
- 2. The attempt recorded a terminal status of `error`, `timeout`, or `not-run` with a concrete reason (tool error message, timeout duration, or external blocker).
83
- 3. The reason is logged via `okstra error-log append-observed --error-type cli-failure ...` (or `tool-failure` if the failure was internal).
84
- 4. **The user permitted it.** Ask at a gate, and have the answer written to this run's `user-responses/` sidecar as a `## REPORT AUTHORING` block with `- Status: approved` (`okstra user-response write --report <data.json> --answers '[]' --report-authoring '{"status":"approved","reason":"<the user's words>"}'`). A failed dispatch is your cue to ask, not your permission — a lead that dispatches once, watches it fail, and proceeds has still decided alone. You cannot write this block for yourself; it is the user's answer, in the user's file.
85
- 5. **Record it in the report.** Set `header.leadAuthoredFallback` with `dispatchFailureReason` quoted verbatim from the failed dispatch row and `approvalSidecar` set to that sidecar's project-relative path. The approval passes the gate; it does not retire it. This block renders under `Report Author`, so someone reading the report months later sees that this run left the report-writer path and why, without having to find the sidecar.
86
-
87
- Speculative reasons such as "session resume constraint", "runtime state is unavailable", or "lead can do it faster" are NOT valid.
88
-
89
- **Enforced:** `validators/validate-run.py` `_validate_lead_authored_report` checks all of it — the terminal failure status, its reason, the approving sidecar block, and the `header.leadAuthoredFallback` record whose reason must match the dispatch row. `release-handoff` is exempt because it never dispatches a report writer.
90
-
91
-
92
- ## Phase 6 → Phase 7 execution sequence (BLOCKING order)
93
-
94
- Phase 6 first produces the report record at `runs/<task-type>/reports/final-report-<task-type>-<seq>.data.json`. Token Usage cells are `null` at this point, and Section 3 does not yet include auto-spawned follow-ups.
95
-
96
- For an implementation-planning run, the Report writer worker owns the Phase 6 design assessment snapshot: it writes `designPreparation` and every stage's `designSurfaceCoverage` into data.json from the detector output and consolidated plan. It does not create user inputs, consume a user answer as if it were part of that snapshot, or materialize `design-prep-requests/`; `schemas/final-report-v2.0.schema.json` and `validators/validate-run.py` `_validate_design_prep_contract` enforce the snapshot shape, detector coverage, and references.
97
-
98
- ### Before `report-finalize`: the translation sidecar (BLOCKING order)
99
-
100
- The finalization renderer overlays the translation sidecar, so a non-English run must produce that sidecar before finalization. Use this fixed order:
101
-
102
- 1. **For a non-English report only**, run `okstra report-finalize ... --only project-activity --only check-source`. The shared finalizer projects canonical activity before checking the English source. A historical manifest without `activityContractVersion: 1` leaves data.json unchanged.
103
- 2. **Only when that check passes**, dispatch the translator worker, which writes `final-report-<task-type>-<seq>.i18n.<lang>.json`.
104
- 3. Run the full `report-finalize` command below. English reports start here; the finalizer repeats the idempotent projection and source check before every downstream step.
105
-
106
- For step 2, write translator-only task instructions and run `okstra
107
- agent-prompt materialize` with `--audience translator`, `--assignment-ref
108
- translator`, `--worker-id translator`, and `--dispatch-kind translator`. Run
109
- `okstra agent-prompt verify` on the returned `metadataPath` before dispatch and
110
- use the returned `promptPath` unchanged. A native-session call uses only
111
- `hostModelValue`; first run `okstra agent-prompt record-dispatch` with the run
112
- manifest, metadata path, and `--enforcement-mode
113
- host-native-spec-link-gate`, then run `okstra agent-prompt link-result` with
114
- `--dispatch-id <invocationId>:attempt-1` and the translation result before
115
- accepting it. A CLI-wrapper call uses `okstra team dispatch` when `terminalBackend` is
116
- `cmux-pane`, or `okstra worker-dispatch` and its `modelExecutionValue`
117
- otherwise. The host-native record links the accepted result to a
118
- verified call specification but does not assert that Okstra observed the host's
119
- actual prompt delivery.
120
-
121
- **Never dispatch the translator before step 1.** The data.json is the English SSOT; a report-writer that authored it in the reader's language produces a translation *from that language into itself* — a full-cost, entirely useless artifact, and the run still fails at `check-source` afterwards. **Enforced:** `okstra report-translate extract` refuses to build a work list from a data.json over the Korean-prose limit, so a mis-ordered dispatch fails at the translator's first command instead of after it. When it does fail, the fix is a report-writer rewrite in English — discard the sidecar and `translation-source.json` produced from the Korean draft rather than editing them, because their English column is not English.
122
-
123
- Phase 7 post-processing is then **one command**. `okstra report-finalize` owns the ordered sequence — it is the same code path the Codex lead adapter runs automatically, so a Claude-led run and a Codex-led run finalize identically:
124
-
125
- ```bash
126
- okstra report-finalize \
127
- --project-root <project_root> \
128
- --run-manifest <runDirectoryPath>/manifests/run-manifest-<task-type>-<seq>.json \
129
- --report <runDirectoryPath>/reports/final-report-<task-type>-<seq>.data.json
130
- ```
131
-
132
- Do NOT run the seven steps below by hand. Hand-running them is the recurring root cause of reports shipping with stale activity, `--` token cells, a missing html sibling, Section 3 missing follow-up entries, or Section 4 rows never spawning — the order is load-bearing and a skipped step surfaces only later, as a validator `contract-violated`. Every step is idempotent, so after fixing a reported failure just re-run the same command.
133
-
134
- The steps it executes, in this contractual order, and the contract each one carries:
135
-
136
- 1. **`project-activity` — project canonical activity.** Replaces only `agentActivity[]` from this run's canonical events before translation source extraction. A legacy manifest without activity contract v1 is a byte-preserving no-op. Conformance compares IDs, order, and every core field against the canonical events.
137
- 2. **`check-source` — verify the data.json is English.** The same gate as the pre-translator check above, run again here because everything after it derives from the data.json: rendering a Korean SSOT into English chrome, spawning follow-ups from it, and validating it all succeed on a record the next phase cannot read. A failure here means the report-writer authored in the reader's language; re-dispatch it with the English rule rather than editing the data.json by hand.
138
- 3. **`token-usage` — collect usage.** Aggregates `leadUsage` / `workers[].usage` / `usageSummary` into team-state and populates `tokenUsage` and the execution-status usage fields in data.json. It does not render the full reading copy.
139
-
140
- The data.json paths populated: `tokenUsage.lead.{totalTokens,billableTokens,costUsd}`, the `worker` / `grand` rows, `tokenUsage.cli.costUsd`, and each `executionStatus[].{totalTokens,billableTokens,costUsd,durationMs,cliTotalTokens,cliCostUsd}` for rows whose role matches a team-state worker. The data.json MUST already exist (Phase 6 output).
141
-
142
- For implementation-planning, this Phase 7 canonical render calls `materialize_design_prep_requests()` after token substitution and creates deterministic request files only for `provisional` / `blocked` items. Later answers are append-only user-input sidecars; request generation and user input never rewrite the assessment fields, so the source report remains immutable as the design-input snapshot after this render. `validators/validate-run.py` `_validate_design_prep_requests` enforces request existence, canonical path, content, and assessment fingerprint.
143
- 4. **`render-views` — render the human report artifact.** Runs against the substituted v2 data.json and its Markdown sibling.
144
-
145
- Output (idempotent — re-running overwrites):
146
- - `runs/<task-type>/reports/final-report-<task-type>-<seq>.html` — single-file self-contained human view, always generated for schema v2 from the dedicated template registered for that task type. Clarification rows with `Status` ∈ {`open`, `answered`} embed response controls and export a `user-response-<task-type>-<seq>.md` sidecar. The original data and Markdown artifacts are never mutated by user input.
147
- - the implementation-planning report renders a **Plan Approval** section at the end of the body — an implementation-option `<select>` plus approval checkbox for legacy candidate-comparison, and an approval checkbox only for selected-direction plans. It stays disabled while any §1 `Blocks: approval` row is unresolved.
148
- - Schema-v1 and quick compatibility reports retain the legacy conditional HTML path; this does not change the schema-v2 always-generated contract.
149
-
150
- It runs after usage collection so token placeholders are substituted in any rendered html, and before routing persistence so the html artifact, when generated, exists for the validator step that checks it. It also overlays the translation sidecar, which is why a non-English run must dispatch the translator before this command — see the ordering rule above.
151
- 5. **`spawn-followups` — routing and follow-up persistence.** Turns the report's `## 4. Follow-up Tasks` rows into `tasks/<task-group>/<new-task-id>/` stubs.
152
-
153
- Behaviour contract:
154
- - Idempotent: rows whose target dir exists are reported as `existing` and skipped. Reruns of the same parent task are safe.
155
- - Rows with `autoSpawn != "yes"` are reported as `skipped` and never written; surface them in Section 3 if manual action is still needed.
156
- - Rows whose `origin` is `phase-continuation` are reported as `skipped (no new task dir)` and never spawn — they advance the same task-key via `/okstra-run` instead.
157
- - An invalid `origin`, `suggestedTaskType`, missing `title`, missing `reason`, or missing `newTaskId` exits `1`. (Schema validation in Phase 6 catches most of these before the spawner runs.)
158
- - **Canonical spawn rule (single source of truth):** the spawner runs when `task-type` ∈ {`implementation`, `final-verification`, `release-handoff`}, OR when `followUpTasks` is non-empty for any other task-type. For the listed task-types `followUpTasks` must be present (schema enforces the phase-continuation row for non-terminal task-types); an empty array is permitted only for `release-handoff`. Missing arrays are no-ops (exit `0`). All other references to this rule (including the Persistence Checklist) defer to this statement.
159
- After the spawner, the report writer MUST append one row per newly spawned task-key with its entry command:
160
-
161
- ```
162
- - Follow-up: `<task-group>/<new-task-id>` — in-session `/okstra-run task-key=<task-group>/<new-task-id> task-type=<suggested>` / standalone `scripts/okstra.sh --task-key <task-group>/<new-task-id> --task-type <suggested>`
163
- ```
164
-
165
- The status file is written after routing and follow-up persistence completes.
166
- 6. **`validate-run` — validate the finished run.** Checks the completed artifact set, including exact canonical-event-to-`agentActivity[]` conformance and the report-views contract that catches a missing or stale html sibling. A failure here names the specific contract; fix it and re-run `okstra report-finalize`.
167
-
168
- 7. **`teardown-stages` — reclaim the stage worktrees.** In-process, and a no-op for every run except a whole-task `final-verification` whose verdict clears the work for release (`accepted`, or `conditional-accept` with no condition blocking release). Whole-task entry merges the done stages but deliberately leaves their worktrees and registry stage-keys in place, because a `blocked` verdict routes straight back to rework on those trees. This step is where they are reclaimed once the verdict says the work is moving on. A stage worktree with uncommitted changes is preserved; stage branches are never deleted.
169
-
170
- After `okstra report-finalize` reports `"ok": true`, **execute the run-scoped cleanup gate.** Call `shutdown_workers` only after that success, all persistence work, and explicit user approval under [okstra-lead-contract](./okstra-lead-contract.md) "Run-scoped worker-resource lifecycle". If the user keeps resources, leave the selected adapter's resources intact and surface its manual cleanup guidance.
171
-
172
- ## Schema-v2 report data responsibilities
173
-
174
- The binding authoring shape is the task bundle's `instruction-set/final-report-schema.json`. Populate both audiences in data.json: agent-facing verdict, routing, evidence, task facts, and audits; user-facing `humanSummary` and task `userNarrative`. The full reading copy template deliberately omits the full user narrative, while the task-specific HTML deliberately moves `crossVerification`, `executionStatus`, and `tokenUsage` into collapsed audit details.
175
-
176
- ## Legacy schema-v1 Markdown structure reference
177
-
178
- The remaining numbered-section guide exists only for rendering or diagnosing historical schema-v1 data. New report-writer runs do not author against it; their instruction-set schema and full reading copy template are authoritative.
179
-
180
- ### Report Header
181
-
182
- Read the exact permitted values for `header.reportOwner` and `header.reportAuthor` from the task bundle's `instruction-set/final-report-schema.json` excerpt, then write those values according to the actual authorship path. The current v2 contract uses the provider-neutral `Okstra lead`; a legacy v1 excerpt may retain historical compatibility values. Do not derive either header field from the selected runtime's provider-specific lead label. Runtime identity remains visible in the execution-status row and team-state audit fields.
183
-
184
- ```markdown
185
- # <task-key> - Multi-Agent Cross Verification Final Report
186
- - Date: <ISO 8601 timestamp>
187
- - Task Key: <task-key>
188
- - Task Type: <task-type>
189
- - Report Owner: `<exact schema header.reportOwner compatibility value>`
190
- - Report Author: `<exact schema header.reportAuthor compatibility value for the actual authorship path>`
191
- - Lead model: `<lead-model>`
192
- - Preparation Method: Final report authored by Report writer worker (or lead-authored fallback — record the documented dispatch failure reason here when applicable; the machine-readable record is `header.leadAuthoredFallback`, which is what the validator and the rendered report both read)
193
- ```
194
-
195
- ### Agent-Specific Execution Status Table
196
-
197
- The template renders this table in the merged record-meta shape (same pattern as §1 / §2.1): one meta cell stacking the short fields, one findings cell. The writer supplies the per-agent fields in data.json; the rendered form is:
198
-
199
- ```markdown
200
- | Record | Summary of Key Findings |
201
- |--------|-------------------------|
202
- | **<team-state.lead.agent>**<br>Role: <team-state.lead.role><br>Model: <model><br>Status: completed<br>Raw tokens: <tokens><br>Billable: <billable><br>Cost: <cost><br>Duration: <duration> | Final synthesis status |
203
- | **<team-state.workers[].agent>**<br>Role: <team-state.workers[].role><br>Model: <model><br>Status: <status><br>Raw tokens: <tokens><br>Billable: <billable><br>Cost: <cost><br>Duration: <duration> | Key findings summary |
204
- ```
205
-
206
- Table Generation Rules:
207
- - **Exactly one row per team-state entry: the lead, then one per `team-state.workers[]` — never more, never fewer.** The first row's agent / role / usage come from `team-state.lead.agent` / `team-state.lead.role` / `leadUsage`. Each later row copies its `agent`, `role`, and `model` **verbatim from a `team-state.workers[]` entry** (e.g. role `Claude worker`, `Codex worker`, `Report writer worker`), ordered to follow `recommendedWorkers` / `resultContract.requiredWorkerRoles`. Do not infer labels from provider names.
208
- - **Never split one worker into multiple function rows or invent role labels** (`Analysis verifier`, `Acceptance critic`, …). A provider that played several roles — e.g. codex ran both the analysis-verify pass and the acceptance-critic pass — is still **one** `team-state.workers[]` entry with **one** usage figure: name the roles it played in that row's *Summary of Key Findings* cell, not in extra rows. Extra or renamed rows break Phase-7 token attribution, which keys each row's tokens to a single team-state worker — a split or renamed row lands as `--`.
209
- - **Raw tokens** = `usage.totalTokens` (input + output + cache_creation + cache_read; the raw volume processed).
210
- - **Billable tokens** = `usage.billableEquivalentTokens` (cache reads weighted at 0.1×, cache_creation 1.25×, output 5×; useful as a single number for "how big was this session in cost terms").
211
- - **Cost (USD)** = `usage.estimatedCostUsd`. For Codex/Antigravity workers that actually invoked the CLI, append `(+ CLI $X.XX)` from `usage.cliEstimatedCostUsd`.
212
- - For Codex/Antigravity workers, append `(CLI: <cliTotalTokens>)` to the Raw tokens cell when `usage.cliTotalTokens` is set.
213
- - Status values are retrieved from team-state; format duration as `Xm Ys` from `usage.durationMs`.
214
- - Workers with status `not-run` or unavailable usage evidence show `--` for tokens/cost/duration; quote the `note` underneath the table if useful.
215
-
216
- ### Token Usage Summary Section
217
-
218
- Place this section immediately after the execution status table.
219
-
220
- Example (the Markdown renders in English whatever `meta.reportLanguage` says):
221
-
222
- ```markdown
223
- ### Token Usage Summary
224
-
225
- | Item | Raw tokens | Billable tokens (input-equiv.) | Cost (USD) |
226
- |------|------------|--------------------------------|------------|
227
- | Lead | 10,479,327 | 1,769,798 | $26.55 |
228
- | Worker subtotal | 7,988,699 | 2,075,192 | $25.43 |
229
- | **Grand total** | **18,468,026** | **3,844,990** | **$51.97** |
230
- | Codex/Antigravity CLI add-on | | | $4.12 |
231
-
232
- > **How to read**: "Raw tokens" is the total tokens the model actually processed (input + output + cache_creation + cache_read). In long sessions cache_read can account for 95%+ making the number look large. "Billable tokens" weights cache_read at 0.1×, cache_creation at 1.25×, and output at 5× to give an input-equivalent figure closer to actual cost. Costs are estimates based on published Anthropic/OpenAI/Google pricing.
86
+ ```json
87
+ {"candidateDetailBoundary":{"expectedChangeAreas":"direction-level-only","expectedVerification":"direction-level-signals-only","forbidden":["exact-file-lists","stage-lists","test-commands"]}}
233
88
  ```
234
89
 
235
- Token Summary Generation Rules:
236
- - **You populate the data.json in Phase 6, BEFORE Phase 7 runs the collector.** Set `tokenUsage.lead.totalTokens` / `.billableTokens` / `.costUsd`, the `worker` and `grand` rows, `tokenUsage.cli.costUsd`, and each `executionStatus[].{totalTokens,billableTokens,costUsd,durationMs,cliTotalTokens,cliCostUsd}` to JSON `null`. The renderer emits `--` for nulls; `okstra-token-usage.py --substitute-data` populates them in Phase 7 and re-renders the markdown. Never set these cells to `0`, `"not-collected"`, `"--"`, `"N/A"`, or any other sentinel: nulls are the only valid placeholder, and the substitution step depends on them being null when it runs.
237
- - Set `meta.reportLanguage` to the resolved `en` or `ko` value passed in
238
- **Report Language**. `auto` is forbidden in this field — the lead has
239
- already resolved it. The field records **which language the human HTML
240
- renders in**, not the language you author in: you always author English
241
- (see "Writing Guidelines" below). Phase 7 reads it to decide whether to
242
- dispatch the translator.
243
- - All values come from `usageSummary` (populated by `scripts/okstra-token-usage.py` at the start of Phase 7). Do not estimate or invent.
244
- - **Lead** row: `usageSummary.leadTotalTokens` / `usageSummary.leadBillableEquivalentTokens` / `usageSummary.estimatedCostUsd.lead`.
245
- - **Worker subtotal** row: `usageSummary.workerTotalTokens` / `usageSummary.workerBillableEquivalentTokens` / `usageSummary.estimatedCostUsd.claudeWorkers`.
246
- - **Grand total** row: `usageSummary.grandTotalTokens` / `usageSummary.grandBillableEquivalentTokens` / sum of `lead + claudeWorkers`.
247
- - **Codex/Antigravity CLI add-on** row: `usageSummary.estimatedCostUsd.cliWorkers`. The writer only supplies the value in data.json — the renderer emits the row when the CLI cost is > 0 and omits it entirely at 0 (`final-report.template.md` gates it on `tokenUsage.cli.costUsd > 0`), so do not expect a `$0.00` row in the rendered report.
248
- - Format tokens with comma separators (e.g., `32,500`); format USD with two decimals (e.g., `$1.43`).
249
- - If `lead` or any `worker.usage` records unavailable evidence, show `--` for that row and append a one-line note (`reason: <note>`).
250
- - If pricing for a model is unknown, the script omits `estimatedCostUsd` for that block — show `N/A` in that column and add a note like `pricing missing for model <model>`.
251
-
252
- ### Implementation-planning section heading contract (schema v1 only)
253
-
254
- **This does not apply to any run you will author.** New runs are schema v2 (`report_contract.CURRENT_REPORT_SCHEMA_VERSION`), and `validate_phase_boundary` returns before the substring scan when `schemaVersion == "2.0"` — the v2 deliverable is gated by the schema instead, whose `implementationPlanning` block requires every one of these contents as a named key. The v2 full reading copy template carries nine headings and serialises the plan as JSON beneath them, so it cannot produce these strings and is not expected to.
255
-
256
- Reading this section as a live instruction is a known and expensive mistake: the writer is sent to author headings the v2 template has no place for, and the run reads as structurally unpassable when nothing is wrong with it. It is retained only for rendering or diagnosing historical schema-v1 reports.
257
-
258
- For those v1 reports, the final report must contain section headings whose **lines include each of the 12 literal English substrings below**. The rows mirror `PLANNING_REQUIRED_SECTIONS` in `validators/validate-run.py`, which is the SSOT. **Enforced:** `tests/contract/test_planning_required_sections_ssot.py` fails when this table and that tuple diverge, and pins the v1 scoping above so the section cannot silently become unconditional again.
90
+ For historical schema-v1 Markdown only, the following heading table remains a read-side compatibility reference. It is not an authoring instruction for report contract 3.0.
259
91
 
260
92
  | # | Required substring | Recommended heading form |
261
- |---|--------------------|--------------------------|
262
- | 1 | `Option Candidates` | `### Option Candidates (Korean gloss)` |
263
- | 2 | `Trade-off` | `### Trade-off Matrix (Korean gloss)` |
264
- | 3 | `Recommended Option` | `### Recommended Option (Korean gloss)` |
265
- | 4 | `Stage Map` | `## 5.5 Stage Map (Korean gloss)` — the per-stage sections hang off it. |
266
- | 5 | `Stepwise Execution Order` | `### Stepwise Execution Order (Korean gloss)` |
267
- | 6 | `Dependency` | `### Dependency / Migration Risk (Korean gloss)` |
268
- | 7 | `Validation Checklist` | `### Validation Checklist (Korean gloss)` |
269
- | 8 | `Rollback` | `### Rollback Strategy (Korean gloss)` |
270
- | 9 | `Requirement Coverage` | `### Requirement Coverage (Korean gloss)` |
271
- | 10 | `Plan Body Verification` + `Gate result:` | `### Plan Body Verification (Korean gloss)` containing a `Gate result:` line — copy `templates/reports/final-report-v2.template.md §5.5.9` verbatim. Validator checks both substrings. |
272
- | 11 | `Cross-Project Dependencies` | `### Cross-Project Dependencies (Korean gloss)` — `_(none)_` when the plan is single-project. |
273
- | 12 | `Decision Drafts` | `### Decision Drafts (Korean gloss)` |
274
-
275
- **Never author a `User Approval Request` section.** The approval gate moved to the YAML frontmatter `approved: true|false` field, and `validators/validate-run.py` fails any report containing a `## User Approval Request` block or a `### 5.5.8 User Approval Request` stub. This paragraph replaces an earlier row that listed the heading as required — following it produced a report the validator rejected.
276
-
277
- `Implementation Design Preparation` is NOT in the substring list: `templates/reports/final-report-v2.template.md` §5.5.10 renders that heading from `implementationPlanning.designPreparation`, so it is enforced by the schema + renderer rather than by a heading scan.
278
-
279
- The English keyword is mandatory and the body of each section is written in English, like everything else you author — see "Writing Guidelines" below. For non-`implementation-planning` runs, omit this entire block — these headings are NOT validator-checked for other task-types.
280
-
281
- The final-report template `templates/reports/final-report-v2.template.md` Section 5.5 already encodes this contract — copy that block verbatim and fill in.
282
-
283
- ### Self-fix rewrite (plan-body self-fix round)
284
-
285
- Lead instructs a self-fix round as **cause groups**, not a flat `P-*` list (`plan-body-verification.md` §"Round protocol" step 7). Treat each group's `causeSummary` as the unit of work: remove that cause once, then confirm every derivative item it names is settled by that single correction. Correct the plan sections the group's items point to — do NOT rewrite the entire draft:
286
-
287
- - abbreviated path → replace with the full relative path from `<PROJECT_ROOT>` (File Structure / Stepwise `files`).
288
- - prose command → make it concrete as an executable command sequence including the working directory, arguments, and task-type.
289
- - placeholder (e.g. `stage-<N>`) → replace with concrete filenames / a regeneration plan.
290
- - requirement-coverage mapping error → remap to a real option/stage/step or XP row.
291
- - missing/weak design-prep contract → fix with kind-specific inline detail or an AI-prepared PREP item carrying a concrete `aiProposal`. Do NOT invent facts requiring user/external authority — keep them `blocked` + request material.
292
-
293
- **Carry the correction to its contradictions (BLOCKING).** "Only the section the item points to" bounds *which defect you fix*, not *how far the fix reaches*. When a correction changes a constant, an owner, a path, or a disposition, every other statement in the plan asserting the old value is now false — find and rewrite those too, in whatever section they sit.
294
-
295
- **Enumerate before you edit.** Patching at the positions the lead named is what makes a round trade one defect for another: the correction lands, its siblings keep asserting the old value, and the next round finds a *new* contradiction the fix itself created. So for each cause group, first list every place the plan mentions that decision — grep the constant, the symbol, the path, the requirement ID across the whole plan body including rejected options, per-stage `Test case (…)` lines, `Acceptance`, `exitContract`, `stageValidation`, and the Requirement Coverage row — then reconcile each hit against the new decision and only then write. `okstra plan-items derivations --data <data.json> --response <user-response sidecar>` does that grep mechanically: it extracts the symbols, paths, and ids the answer names and returns every plan string that mentions one, as a pointer plus excerpt. Its output is candidates, not verdicts — which hits are now false is yours to decide — but starting from it is what stops the enumeration from being skipped, which is the observed failure (17 of 23 blocked items in one run were a recorded decision whose derivations were never swept). Record the enumeration in the group's supersession entry so the next round can see what was considered in scope. A patch that leaves its own contradictions standing produces the same defect class in the next round, so the loop spends its budget re-finding what the previous round created. Record each retirement in `implementationPlanning.supersessionLedger[]` exactly as the answer-carry-in rule requires (`_common-contract.md` §"Supersession").
296
-
297
- **Record the reach.** For each cause group you rewrite, list the data.json paths you actually changed in that group's `rewrittenPaths`, and the subset of those lying outside the sections its `itemIds` point at in `outsideScopePaths` (e.g. `stages[0].stepwiseExecution`, `validationChecklist[3]`). Carrying a correction to its contradictions legitimately reaches past the flagged item, so the second list is a measurement and not a violation — no threshold is applied to either. It exists because "this round was a targeted correction, not a full regeneration" is currently a claim with nothing behind it, and a round that quietly rewrites the whole draft costs the same tokens every time it repeats. **Enforced:** `validators/validate-run.py` `_validate_self_fix_rewrite_scope` requires every `outsideScopePaths` entry to appear in `rewrittenPaths`.
298
-
299
- After correcting, leave `self-fixed in round <N>: <what was fixed>` in that `planItems[].selfFixNote`. Do NOT set `planBodyVerification.selfFixRoundsApplied` — the lead owns that count and records it at the round's end, so promotion eligibility does not hang on a worker's self-report. `needs-user-input` items are NEVER a correction target — they are promoted to clarification as-is.
300
-
301
- Lead may instruct several rounds (bounded by `selfFixMaxRounds`, see [plan-body-verification](./plan-body-verification.md) §"Round protocol" step 7). Each round targets only the items still broken, so a round that fixes nothing new ends the loop — correcting an item by breaking a sibling counts as no progress, not progress.
302
-
303
- ### Final-verification verdict token contract (BLOCKING)
304
-
305
- When the run's `task-type` is `final-verification`, the report's `## 7. Final Verdict` table MUST contain a `Verdict Token` row whose value is **exactly one of** the literal strings below. The `release-handoff` profile reads this row as its entry gate; any other value blocks the next phase.
93
+ |---:|---|---|
306
94
 
307
- | # | Required substring | Meaning |
308
- |---|--------------------|---------|
309
- | 1 | `accepted` | All acceptance criteria pass; `release-handoff` may proceed. |
310
- | 2 | `conditional-accept` | Acceptance passes with caveats; user must resolve listed conditions before `release-handoff`. |
311
- | 3 | `blocked` | Acceptance failed; routing returns to `error-analysis`, `implementation-option-selection`, or `implementation-planning` according to whether the cause, direction, or detailed plan failed. |
312
-
313
- For every other task-type, set the `Verdict Token` cell to `not-applicable`. Do NOT omit the row — the template renders it for all task-types and downstream tooling expects the field to exist.
314
-
315
- The final-report template `templates/reports/final-report-v2.template.md` Section 7 already encodes this contract — copy that block verbatim and fill in.
316
-
317
- ### Release-handoff section contract (release-handoff runs only)
318
-
319
- When the run's `task-type` is `release-handoff`, the final report MUST include Section `## 5.6 Release Handoff Deliverables` with all eight sub-sections (`5.6.1` Source Verification Report, `5.6.2` Feature Branch & Working-Tree State, `5.6.3` User Selections, `5.6.4` Executed Commands, `5.6.5` Commit List, `5.6.6` Merge Conflict Probe, `5.6.7` Pull Request Outcome, `5.6.8` Routing Recommendation). Every entry is dictated by the lead's recorded git/gh command log and the user's verbatim answers to the H1/H2/H3 menu prompts. H1 choices are `local checkout`, `push + PR`, or `skip`; release-handoff records existing implementation commits and MUST NOT create new commits. If the user picked `skip` (H1) or `cancel` (H3), keep 5.6.3 populated but leave 5.6.4–5.6.6 explicitly empty per the template's empty-state lines.
320
-
321
- **Single-lead authorship (release-handoff only):** release-handoff has no worker roster. The lead authors the final-report file directly — there is no `Report writer worker` dispatch to perform in Phase 6, no resume-safe dispatch concern, and no mandatory worker-results file for a report-writer role. The rest of this contract's dispatch / resume / fallback machinery applies ONLY when `Report writer worker` is in the roster (i.e. every task-type other than `release-handoff`).
322
-
323
- The final-report template `templates/reports/final-report-v2.template.md` Section 5.6 already encodes this contract — copy that block verbatim and fill in. For non-`release-handoff` runs, omit Section 5.6 entirely.
324
-
325
- ### Mandatory worker-result pointer and audit sidecar (BLOCKING)
326
-
327
- You (the report-writer worker) MUST write the worker-result pointer at `**Worker Result Path:**`, defaulting to:
328
-
329
- ```
330
- runs/<task-type>/worker-results/report-writer-worker-<task-type>-<seq>.md
331
- ```
332
-
333
- Its body contains exactly the project-relative data.json path and the convergence-state input path. Analysis-worker result files stay in `## Inputs`; do not copy their list into the pointer. **Enforced:** both dispatch adapters include this pointer in `WorkerJob.completion_paths` and refuse `completed` while it is absent.
334
-
335
- The pointer's frontmatter and header follow `team-contract` "Result Frontmatter" and the standard worker-result header sections. Use `workerId: "report-writer"` and copy the remaining canonical values from `analysis-material.md`; do not duplicate the final-report body.
336
-
337
- You MUST also write the separate heartbeat/read-confirmation audit file at `**Audit sidecar path:**`, derived from Worker Result Path and defaulting to:
338
-
339
- ```
340
- runs/<task-type>/worker-results/report-writer-worker-audit-<task-type>-<seq>.md
341
- ```
342
-
343
- The selected report-writer preamble defines that audit shape. **Enforced:** `validators/validate_session_conformance.py` checks its reading confirmation, progress stages, timestamps, and cadence whenever the role completes.
95
+ ## Phase 6 Phase 7 execution sequence (BLOCKING order)
344
96
 
345
- ### Main Body Section
97
+ Do not run the seven steps below manually. Invoke `okstra report-finalize`; contract 3.0 runs them in this order:
346
98
 
347
- Section numbering follows `templates/reports/final-report-v2.template.md` exactly that file is the documentation SSOT for section names and ordering. For full body structure at authoring time, consult your run's **phase-stripped** `final-report-template.md` (the per-task-type instruction-set copy defined in Phase 6 dispatch item 10); the "copy that block verbatim" references below mean the §-block as it appears in that stripped copy, not a re-read of the full source.
99
+ 1. **`token-usage`**collect usage into team state without touching the final record.
100
+ 2. **`project-activity`** — report assembly validates every owner input and publishes the final record once.
101
+ 3. **`check-source`** — verify the assembled English source.
102
+ 4. **`render-views`** — render the Markdown reading copy and human HTML.
103
+ 5. **`spawn-followups`** — materialize registered follow-up tasks.
104
+ 6. **`validate-run`** — validate the record, views, run manifest, and team state.
105
+ 7. **`teardown-stages`** — remove eligible stage worktrees after successful validation.
348
106
 
349
- **Verdict Card (top-of-report, mandatory).** Render `## Verdict Card` between the report header and the (conditional) Approval block. Its `Verdict Token` and `Direction` cells MUST byte-match the corresponding cells in `## 7. Final Verdict`. Its `Next Step`, the final-verdict next step, and the first item of `## 3. Recommended Next Steps` MUST name the same route target, though the actionable command and prose need not be byte-identical. Divergence is `contract-violated`.
107
+ ### Before `report-finalize`: the translation sidecar
350
108
 
351
- **Background and Rationale (top-of-report, mandatory every task-type).** Fill the data.json `rationale` object (rendered as `## Background and Rationale`, right after the Verdict Card). It is the reviewer-facing narrative that answers four questions, in order write each as **prose**, not a table:
352
- - `motivation` — why we are doing this work (goal / context).
353
- - `problem` — why this is a problem (the flaw in the current state).
354
- - `approach` — what work is therefore needed (the chosen direction).
355
- - `justification` — why this is a reasonable choice (versus alternatives, with rationale).
109
+ Never dispatch the translator before report assembly and `check-source`. For a non-English human report, first run `report-finalize --only token-usage --only project-activity --only check-source`; the extraction command refuses to build a work list from a non-English source. Then dispatch the translator worker with `okstra agent-prompt materialize --audience translator`, `okstra agent-prompt record-dispatch`, `okstra worker-dispatch --audience translator`, and `okstra agent-prompt link-result`. Resume with `report-finalize --only render-views --only spawn-followups --only validate-run --only teardown-stages`; do not assemble the record a second time.
356
110
 
357
- Every field MUST anchor its claim with at least one evidence reference — a `path:line`, an **in-report** ID (`C-001` / `E-006` / `§5.4`), or another cited source already present in this report. When the evidence is genuinely insufficient, say so explicitly (`insufficient evidence — <reason>`) instead of inventing one. `validators/validate-run.py` → `_validate_rationale_evidence` fails any field carrying neither a citation nor an insufficiency marker, so an ungrounded or fabricated rationale is rejected at validation time.
111
+ ## Routing pointer
358
112
 
359
- **Reader-facing prose MUST NOT cite a bare brief/worker-internal ID that this report never surfaces** — `RC-*` (reporter confirmations, defined in the brief), `RF-*` / `F-*` (findings, defined in worker-results) have no anchor in the final report, so a reader hits an opaque token with nothing to click. Either expand it inline (`the confirmed version target 1.27.47→1.27.48`) or, for an audit trail, namespace it (`claude:F-005`). **Enforced:** `_validate_no_opaque_id_references` fails a bare `RC-*` / `RF-*` / non-namespaced `F-*` appearing in `verdictCard` / `finalVerdict` / `rationale` / `clarificationItems[].statement`. (The renderer anchors + links in-report IDs of any digit width, so a surfaced `RC-4`-style id does resolve.) Do NOT restate the Verdict Card or the §5.4 trade-off matrix verbatim — this section is the *why*, in connected prose, that those tables compress.
113
+ `workflow.nextRecommendedPhase` has `phase`, `status`, and `rationale`. The status vocabulary is `ready`, `pending`, `blocked`, and `terminal`.
360
114
 
361
- 0. **Clarification Response Carried In** — render this `## 0.` heading ONLY when `{{CLARIFICATION_RESPONSE_RELATIVE_PATH}}` is non-empty. Walk every `C-*` row of the prior report's `## 1. Clarification Items` table, reconcile against new evidence, and record the outcome (`resolved` / `obsolete`) with citation before drafting the verdict. When no carry-in path was provided, OMIT the `## 0.` heading entirely — the validator fails an empty Section 0 stub. The lead calls `okstra incremental-scope` exactly once, combining answered-clarification stage impacts (`--impacted`) and changed PREP IDs (`--prep-items`); selected-option, Stage Map, or recommended-approach changes pass both CSVs empty to force full mode. Record that single decision JSON verbatim into `implementationPlanning.incrementalDecision` (`mode`, `reverifyStages`, `carryStages`, `reason`); the renderer emits the `### 0.1 Incremental Re-Verification Scope` audit block from it, and the validator fails an `incremental`-mode run whose Section 0 omits that block. In `incremental` mode this run's `planItems` MUST carry every plan-item id from the re-verified stages forward with its updated verdict; if re-verification concludes a plan item should be REMOVED, that is a signal the answer's blast radius is not local — do not drop it here, tell the lead to abandon incremental and re-route to a FULL re-verification, because the carry merge only adds prior items and would resurrect the removed item's stale verdict. After authoring the current data.json, call `okstra incremental-carry`, passing the decision's `carryStages` CSV to `--carry-stages` and its `reverifyStages` CSV to `--reverify-stages`. A `CarryError` means the stage/PREP ownership contract is unsafe: discard the partial merged output and route the run through full re-verification; never publish a partially merged report.
362
- 1. **Clarification Items** — single unified `C-*` table; column schema (4 columns with the short fields stacked in one record-meta cell), ID convention, and rerun behaviour are owned by `_common-contract.md §Clarification request policy` (SSOT). The deprecated `5.5.9 Open Questions` / `1.1 Additional Material Request` / `1.2 User Confirmation Questions` sub-sections are removed; the validator fails reports that reintroduce them.
363
- - **Open `Blocks=approval` rows carry `origin` and `userConfirmation`** (same SSOT). Lead's dispatch prompt MUST state, per intended blocker, which `origin` applies and what Lead did about it — the writer cannot observe either. When Lead instructed the writer to raise an item rather than decide it, that row's `origin` is `lead-directed` no matter how the workers subsequently voted on it: an instruction returning as a consensus is not a finding. Before writing such an instruction, run the confirmation sequence in [okstra-lead-contract](./okstra-lead-contract.md) "User confirmation before an approval blocker" — asking first is usually cheaper than the row.
364
- 2. **Evidence and Detailed Analysis** — primary evidence rows (file path, line, snippet); secondary evidence / alternate interpretations. If `reference-expectations.md` lists explicit expected values, record match/gap per row.
365
- - **Final-verification added-surface audit.** When `header.taskType` is `final-verification`, populate `finalVerification.addedSurfaceAudit` from the workers' enumeration of what the diff added — one row per identifier / module / configuration entry, each with the callers found across the repository and its disposition. Do not summarise the rows away: this table is the only machine-readable evidence for the over-delivery axis, and requirement coverage answers the opposite question. An `over-delivery` row's `note` MUST cite the `AB-NNN` (no caller) or `CA-NNN` (called, but no requirement) row it became, and that row MUST exist in this report. **Enforced:** `schemas/final-report-v2.0.schema.json` requires the array and rejects an unknown `disposition`; `validators/validate-run.py::_validate_added_surface_audit` enforces the citation and refuses a `traced` row that names no requirement.
366
- - **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. When a candidate is a step in a propagation chain rather than a competing explanation — the analysis calls it a downstream step, a second stage, or a consequence of another candidate — set its `downstreamOf` to the ids of the candidates immediately upstream of it; leave the field absent for a candidate that stands on its own. Every id listed MUST be another candidate in the same report, no row may name itself, and the links MUST NOT form a cycle; `validators/validate-run.py::_validate_cause_chain` rejects all three. This is the only place the chain is machine-readable — prose calling a candidate "the second step of the chain" while `downstreamOf` is absent leaves the report's figure claiming the candidates are alternatives. Route `errorAnalysis.routing.nextTaskType=implementation-option-selection` with `direction=begin-option-selection`, 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; `validators/validate-run.py::_validate_error_analysis_consistency` enforces exact target agreement and uniqueness.
367
- - **Implementation-option-selection comparison.** When `header.taskType` is `implementation-option-selection`, populate `implementationOptionSelection` from the converged direction-selection findings. Preserve every merged or rejected raw candidate in `candidateAudit`, and put at most three selectable candidates in `rankedOptions`. Each displayed candidate carries its requirement coverage, scope commitments, criterion scores, feasibility votes, safety blockers, unresolved feasibility facts, planning invariants, and exact coverage summary. In each displayed candidate, `expectedChangeAreas` names direction-level change surfaces, never exact file paths or an exact file list. `expectedVerification` names direction-level verification signals, never a stage list or executable test commands. `schemas/final-report-v2.0.schema.json` enforces the displayed-summary constants and the three-option cap; semantic recalculation belongs to `validators/validate-run.py`.
368
- - **Routing.** `implementationOptionSelection.routing` is a required **string enum** — not an object — with exactly three values: `implementation-planning`, `pending-direction-selection`, `blocked`. It is the only field in this report that records where the task goes next, and Phase 7 projects `workflow.nextRecommendedPhase` from it (`scripts/okstra_ctl/next_phase.py`): `implementation-planning` becomes a `ready` pointer naming that phase, while `pending-direction-selection` and `blocked` become `pending` and `blocked` pointers carrying no phase. Only the first proposes a next run.
369
- - **The value is determined by this run's mode and candidate set, not chosen freely.** `preselected-validation` mode routes to `implementation-planning` — the direction was already selected and this run only validated it. `candidate-comparison` mode that displays any candidate routes to `pending-direction-selection` — the user still owes the direction pick, so a comparison never routes straight to planning. `blocked` is legal only when no valid candidate exists at all, and is required in that case. **Enforced:** `scripts/okstra_ctl/implementation_options.py::validate_implementation_option_selection` rejects all three mismatches (`validated preselected direction must route to implementation-planning`, `candidate-comparison with options must await direction selection`, `routing must be blocked only when no valid options exist` / `routing may be blocked only when no valid options exist`).
370
- - **Implementation-planning direction branch.** When `implementationPlanning.planningContract == "selected-direction"`, read `selectedDirectionRef` and the snapshot before authoring. Materialize the snapshot into `directionRealization`, stages, validation, rollback, and bidirectional original-requirement links. Author exactly one `P-Dir-1`; its payload is the complete `directionRealization`. Its verification covers the core mechanism, architecture boundaries, planning invariants, and any hidden direction change against `selectedDirectionRef`. Do not author Option Candidates, candidate scores, a Recommended Option, or user candidate-selection fields. When current evidence requires changing the direction, author `outcome: "direction-invalidated"` and omit the execution plan. Legacy candidate-comparison reruns retain `P-Opt-*`, Option Candidates, trade-off, and Recommended Option semantics.
115
+ `phase` is non-empty only for an authored `ready` pointer. `prepare` may lower `ready` to `pending` while retaining `phase` in `scripts/okstra_ctl/render.py::_derive_next_recommended_phase`; readers must use `status` for launchability. When `finalVerification.routingRecommendation.target` is `release-handoff(stage-group)`, write `phase` as `release-handoff`.
371
116
 
372
- ```json
373
- {
374
- "candidateDetailBoundary": {
375
- "expectedChangeAreas": "direction-level-only",
376
- "expectedVerification": "direction-level-signals-only",
377
- "forbidden": ["exact-file-lists", "stage-lists", "test-commands"]
378
- }
379
- }
380
- ```
117
+ ## Compatibility
381
118
 
382
- - **Implementation-option-selection is non-terminal.** Its `followUpTasks` includes a `phase-continuation` row with `autoSpawn: "no"` and `priority: "P0"`; the schema's non-terminal conditional enforces row presence.
383
- - **Implementation and final-verification routing.** Both phases record where the task goes next in a `routingRecommendation` **object** with exactly two fields: `target` is one enum value, `rationale` is the sentence that justifies it. Neither field takes free-form routing prose, and a target named only in the prose does not count — Phase 7 projects `workflow.nextRecommendedPhase` from `target` alone (`scripts/okstra_ctl/next_phase.py`), so the value you write there is the route the task actually takes.
384
- - `implementation.routingRecommendation.target` is one of `final-verification`, `error-analysis`, `implementation-planning`, `implementation`. Pick `final-verification` when this stage's plan items landed and validation passed; `error-analysis` when a failure's cause is not understood; `implementation-planning` when the approved plan itself no longer fits the evidence; `implementation` when work remains inside this stage (the next run is a fix run).
385
- - `finalVerification.routingRecommendation.target` is one of `release-handoff`, `release-handoff(stage-group)`, `error-analysis`, `implementation-option-selection`, `implementation-planning`, `implementation`, `done`. Both `release-handoff` values require the `accepted` verdict, and plain `release-handoff` additionally requires `verificationScope` `whole-task` — a `single-stage` accepted run routes to `release-handoff(stage-group)` instead. `done` ends the lifecycle here. `error-analysis` / `implementation-option-selection` / `implementation-planning` follow the cause-vs-direction-vs-plan split of the verdict token table above.
386
- - `rationale` is one or two sentences on why that target and nothing else, citing the blocker ids or evidence rows behind the choice. It is the only free-form half of the field; the digest sections still point here for the full reasoning.
387
- - `releaseHandoff.routingRecommendation` is unchanged — it stays a single prose field, because `release-handoff` is terminal and nothing projects a next phase from it.
388
- - **Enforced:** `schemas/final-report-v2.0.schema.json` rejects a `target` outside the enum, a missing `rationale`, and any string value in either field; `validators/validate-run.py::_validate_final_verification_consistency` rejects a final-verification report whose `routingRecommendation.target` is absent, and rejects the verdict↔routing and scope↔routing combinations named above.
389
- 3. **Recommended Next Steps** — prioritized actions. After Phase 7's follow-up spawner runs, append a row per newly created task-key (see "Phase 6 → Phase 7 execution sequence" above). **Approval-gate consistency:** when §1 carries any `Blocks: approval` row with `Status` ∈ {open, answered}, the Verdict Card `Next Step` and the first recommended step MUST point to the clarification rerun (`resume-clarification` of the SAME task-type) — never to "flip frontmatter `approved: true` → jump straight to `implementation`". Run-prep enforces this gate (`run.py _validate_approved_plan` fail-closes on those rows and on a blocking data.json `gateResult`), so a direct-implementation next-step is an instruction the reader cannot actually follow. **Cross-project pointer rule:** for cross-project dependencies (another repo / a different top-level deployment module / a published package), `crossProjectDependencies` (§5.4 Cross-Project Dependencies) is authoritative — do NOT duplicate that substance (prerequisite work / verification signals / handoff) into `recommendedNextSteps`; put only a one-line pointer to that section (no double-recording).
390
- 4. **Follow-up Tasks** — auto-spawn-eligible table. Each row drives `okstra-spawn-followups.py`; see template §4 for the row schema.
391
- 5. **Missing Information and Risks** — uncertain / "I don't know" items. `implementation-planning` adds §5.5 (see heading contract below); `release-handoff` adds §5.6.
392
- 6. **Cross Verification Results** — 4 categories (Full / Partial / Contested / Worker-Unique) when convergence is enabled, per `convergence`. Prepend the Round History sub-table (columns: `Round | inputQueueSize | resolvedCount | carriedForwardCount | dispatches | skippedWorkers`) plus a `round2SkippedReason: <value>` note, pulled verbatim from `convergence-<task-type>-<seq>.json`. Empty contested list renders as `- No items lacking consensus.`. Convergence-disabled runs use the legacy Consensus/Differences format and omit the round table.
393
- 7. **Final Verdict** — `Direction` ∈ `continue-investigation` / `begin-option-selection` / `begin-planning` / `begin-implementation` / `approve` / `reject` / `hold`. **Verdict Token** is `not-applicable` for every task-type except `final-verification` — see "Final-verification verdict token contract" below for that case.
394
-
395
- **§5.10 Fix History (data-presence gated).** When the run-manifest carries a `fixCycleId`, fill the data.json `fixCycle` block (`cycle` / `targetReport` / `symptom` / `runs`). Read the values from the task root's `history/fix-cycles.jsonl`: `cycle` MUST equal `fixCycleId`, `targetReport` / `symptom` come from that cycle's `opened` row, and `runs` lists its attached `run` rows (`taskType` / `runSeq` / `runManifest`). The validator (`validators/validate-run.py` → `_validate_fix_cycle`) fails the run when the block is missing or `fixCycle.cycle` does not match `fixCycleId`. When the run-manifest has no `fixCycleId`, OMIT the `fixCycle` block entirely — the renderer omits §5.10.
396
-
397
- ### Writing Guidelines
398
-
399
- - Write in Markdown. **Prefer tables over prose bullet lists** for any section that enumerates multiple items with the same shape (evidence rows, risks, options, dependencies, rollback steps, follow-ups, open questions). Bullets are reserved for short, single-line standalone statements (e.g., "- No additional information requested."). When the template provides a table form, do NOT degrade it back to bullets in the rendered report. **Exception — `## Background and Rationale` (`rationale`) is deliberately prose**: it is connected narrative explaining the *why*, not a same-shape enumeration, so write full sentences there rather than forcing it into a table.
400
- - **Do not restate the same conclusion verbatim across sections.** The Verdict Card and Reader Summary are *digests*: give the outcome in one or two sentences and point to `## 7. Final Verdict` / `## 5.8.8 Routing Recommendation` for the full reasoning, rather than copying their multi-clause conclusion word-for-word. Only the `Verdict Token` and `Direction` cells must byte-match §7 (per the Verdict Card contract above). `Next Step` must point to the same routing target as §7, but its actionable command and prose need not be byte-identical. The prose conclusion must not be a duplicate.
401
- - **Keep each sentence to one main idea.** A single sentence that stacks four or five clauses with em-dashes and nested parentheticals (300+ characters) is hard to read, and the renderer can only line-break at sentence ends — so break such reasoning into separate sentences. Facts, evidence, and IDs still live in the tables; prose carries only the connective *why*.
402
- - **Write the report body in English, whatever the Report Language is.**
403
- The data.json is the SSOT every later phase, validator and agent reads,
404
- and its full reading copy has the same audience, so both stay
405
- in one language. Only the human HTML follows the reader: when
406
- **Report Language** is not `en`, Phase 7 dispatches the translator
407
- worker, which writes a sidecar the HTML renderer overlays. You never
408
- author that sidecar and never write a second language into the data.json.
409
- The HTML's own fixed strings — headings, column headers, empty states,
410
- enum labels — come from `templates/reports/html/i18n/<lang>.json` and
411
- are not yours to write either.
412
- - If only one worker is usable, perform a reduced-confidence synthesis
413
- - If evidence is insufficient, explicitly state "I don't know"
414
- - If expected values are present in `reference-expectations.md`, list matches, gaps, and missing evidence separately
415
- - If `reference-expectations.md` is explicitly empty, report the absence of expected states as missing information
416
- - If there are no substantive differences between workers, state "No difference"
417
- - Write the actual analysis text instead of a meta-description
418
- - Do not make unfounded assertions
419
- - Include findings from all four categories. Do not omit "contested" or "worker-unique" findings
420
- - Include the convergence round history sub-table (Section 6) so the reader can audit which rounds executed and what `round2SkippedReason` indicates (e.g. `"not-skipped"` when Round 2 ran, or one of the three skip reasons). Pull values verbatim from `convergence-<task-type>-<seq>.json`; do NOT recompute.
421
- - For each finding, include a brief summary of votes per worker across executed rounds. `verification-error` votes are listed as such — never as `DISAGREE`.
422
- - The report writer worker does not participate in the re-verification vote. It is responsible only for drafting the final report
423
-
424
- ## Artifact Persistence Checklist
425
-
426
- Persistence steps that must be performed in Phase 7:
427
-
428
- - [ ] 1. **Draft report record**: Save to `runs/<task-type>/reports/final-report-<task-type>-<seq>.data.json`
429
- - [ ] 2. **Update team state**: Update `runs/<task-type>/state/team-state-<task-type>-<seq>.json`
430
- - Final status, start/end times, and result file paths for each worker
431
- - Overall run status
432
- - [ ] 3. **Update run manifest**: Update `runs/<task-type>/manifests/run-manifest-<task-type>-<seq>.json`
433
- - [ ] 4. **Update task-manifest.json**: Reflect task-level status and workflow lifecycle metadata
434
- - Update `workCategory` if the run produced a confident classification
435
- - Update `workflow.currentPhase`, `workflow.currentPhaseState`, `workflow.lastCompletedPhase`, and `workflow.phaseStates`
436
- - Write `workflow.nextRecommendedPhase` as an object with exactly three string fields — `phase`, `status`, `rationale`. **This checklist item is the canonical statement of that field**; the lead contract, the context loader and every okstra skill point here instead of restating it, so a change to the rule belongs in this bullet.
437
- - `status` is one of `ready` (the named phase can be started now), `pending` (this run did not settle where the task goes next), `blocked` (something outside this run must change before any phase can start), or `terminal` (the lifecycle ends here; there is no next phase).
438
- - `phase` carries a lifecycle phase name only when `status` is `ready`; under the other three, write the empty string. This is an authoring rule for the value **you** write, not a constraint the struct enforces or a shape you can rely on when reading. `prepare` lowers a `ready` pointer to `pending` and keeps its `phase` (`scripts/okstra_ctl/render.py::_derive_next_recommended_phase`), so every in-flight task's manifest holds a non-`ready` pointer that still names a phase. Never infer launchability from a non-empty `phase` — read `status`, which is the one field that answers it.
439
- - `rationale` is one sentence saying why. It is the only free-form field, and it is the only part of the pointer that survives a correction.
440
- - **`phase` and `status` MUST agree with this report's own routing field.** Which field that is depends on the task-type: `requirementsDiscovery.routing.nextTaskType`, `errorAnalysis.routing.nextTaskType`, `implementationOptionSelection.routing`, `implementationPlanning.outcome`, `implementation.routingRecommendation.target`, or `finalVerification.routingRecommendation.target` (see "Implementation and final-verification routing" above for those two enums). Two task-type groups have no routing field that Phase 7 projects from: `release-handoff` (its `routingRecommendation` is prose that nothing reads) is always `terminal`, and the analysis sidetracks — `improvement-discovery`, `project-analysis`, `feature-analysis`, `change-impact-analysis` — are always `pending`, whatever a per-candidate recommendation inside the report says. One routing value is not a phase name: when `finalVerification.routingRecommendation.target` is `release-handoff(stage-group)`, write `phase` as `release-handoff`. The parenthesised part names the handoff's scope, not a different phase, and Phase 7 projects it that way — writing the parenthesised form here records a correction against a report that was right.
441
- - **Enforced:** Phase 7 validation recomputes `phase` and `status` from that routing field (`scripts/okstra_ctl/next_phase.py::project`). On a passing run whose authored pair disagrees with the recomputed pair, `validators/validate-run.py` overwrites both with the recomputed values, replaces your `rationale` with a pointer sentence, and preserves what you wrote under `workflow.nextRecommendedPhaseCorrection.authored`. A run whose validation fails ends with the pointer `blocked`, keeping your `rationale`. So the routing field is what actually moves the task — a pointer authored against the report body changes nothing but the audit trail.
442
- - Update `workflow.awaitingApproval`
443
- - Update `workflow.lastSafeCheckpoint` to the best resume point for the current task
444
- - [ ] 5. **Update task-index.md**: Refresh human-readable summary
445
- - [ ] 6. **Generate final status file**: `runs/<task-type>/status/final-<task-type>-<seq>.status` (if necessary)
446
- - [ ] 7. **Save convergence state**: `runs/<task-type>/state/convergence-<task-type>-<seq>.json` (when convergence is enabled)
447
- - [ ] 8. **Spawn follow-up task stubs**: run `okstra spawn-followups` against the final-report per the canonical spawn rule defined in "Phase 7 follow-up task spawner" above. Do not restate the trigger condition here — that section is the single source of truth. The script is idempotent across reruns.
448
- - [ ] 9. **Human HTML report** (conditional): `runs/<task-type>/reports/final-report-<task-type>-<seq>.html` — produced by the Phase 7 `render-views` step per its generation predicate (≥1 §1 `C-*` clarification row OR an implementation-planning Plan Approval widget target; that step is the single source for the exact condition). Reports matching neither condition legitimately have no html sibling; do not treat its absence as a missing artifact.
449
-
450
- ### Response after Persistence
451
-
452
- Provide a concise report in the Report Language covering the following:
453
- - Completion status
454
- - Human report path (`.html`)
455
- - Report record path (`.data.json`) and one line to render the full reading copy: `okstra render-final-report <task-qualified data.json>`
456
- - Team-state path
457
- - Validator results
458
- - Resume command path
459
- - Remaining blockers (if any)
119
+ Contract 2.0 reports remain readable. They are never migrated or rewritten by report assembly. New writes use contract 3.0 only.