okstra 0.49.0 → 0.50.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/docs/kr/architecture.md +8 -8
  2. package/docs/kr/cli.md +2 -2
  3. package/docs/project-structure-overview.md +3 -3
  4. package/docs/superpowers/plans/2026-06-05-wizard-batch-prompts.md +559 -0
  5. package/docs/superpowers/specs/2026-06-05-wizard-batch-prompts-design.md +121 -0
  6. package/docs/task-process/error-analysis.md +1 -1
  7. package/docs/task-process/final-verification.md +1 -1
  8. package/docs/task-process/release-handoff.md +1 -1
  9. package/docs/task-process/requirements-discovery.md +1 -1
  10. package/package.json +1 -1
  11. package/runtime/BUILD.json +2 -2
  12. package/runtime/agents/SKILL.md +3 -3
  13. package/runtime/agents/workers/claude-worker.md +1 -1
  14. package/runtime/agents/workers/codex-worker.md +1 -1
  15. package/runtime/agents/workers/gemini-worker.md +1 -1
  16. package/runtime/agents/workers/report-writer-worker.md +3 -3
  17. package/runtime/bin/okstra-render-report-views.py +1 -1
  18. package/runtime/prompts/launch.template.md +1 -1
  19. package/runtime/prompts/profiles/_common-contract.md +11 -11
  20. package/runtime/prompts/profiles/_implementation-deliverable.md +1 -1
  21. package/runtime/prompts/profiles/_implementation-executor.md +1 -1
  22. package/runtime/prompts/profiles/_implementation-verifier.md +1 -1
  23. package/runtime/prompts/profiles/error-analysis.md +1 -1
  24. package/runtime/prompts/profiles/final-verification.md +2 -2
  25. package/runtime/prompts/profiles/implementation-planning.md +9 -9
  26. package/runtime/prompts/profiles/improvement-discovery.md +5 -5
  27. package/runtime/prompts/profiles/release-handoff.md +2 -2
  28. package/runtime/prompts/profiles/requirements-discovery.md +2 -2
  29. package/runtime/python/okstra_ctl/clarification_items.py +11 -11
  30. package/runtime/python/okstra_ctl/render.py +1 -1
  31. package/runtime/python/okstra_ctl/render_final_report.py +1 -1
  32. package/runtime/python/okstra_ctl/report_views.py +12 -12
  33. package/runtime/python/okstra_ctl/run.py +3 -3
  34. package/runtime/python/okstra_ctl/wizard.py +90 -3
  35. package/runtime/python/okstra_ctl/workflow.py +1 -1
  36. package/runtime/skills/okstra-brief/SKILL.md +1 -1
  37. package/runtime/skills/okstra-convergence/SKILL.md +8 -8
  38. package/runtime/skills/okstra-report-writer/SKILL.md +22 -22
  39. package/runtime/skills/okstra-run/SKILL.md +2 -0
  40. package/runtime/skills/okstra-team-contract/SKILL.md +1 -1
  41. package/runtime/templates/reports/final-report.template.md +187 -187
  42. package/runtime/templates/reports/i18n/en.json +4 -4
  43. package/runtime/templates/reports/i18n/ko.json +4 -4
  44. package/runtime/templates/reports/implementation-planning-input.template.md +1 -1
  45. package/runtime/templates/reports/release-handoff-input.template.md +1 -1
  46. package/runtime/templates/reports/user-response.template.md +1 -1
  47. package/runtime/templates/worker-prompt-preamble.md +1 -1
  48. package/runtime/validators/lib/fixtures.sh +2 -2
  49. package/runtime/validators/validate-implementation-plan-stages.py +9 -9
  50. package/runtime/validators/validate-report-views.py +10 -10
  51. package/runtime/validators/validate-run.py +36 -36
  52. package/runtime/validators/validate_improvement_report.py +8 -8
@@ -213,7 +213,7 @@ Every item in sections 1–5 MUST be tagged with the ticket(s) it relates to. Th
213
213
 
214
214
  - **Table-form items**: include a `Ticket ID` column. The column is placed immediately after the row-ID column (e.g. after `F-001`, `M-001`).
215
215
  - **Bullet / numbered-list items**: prepend `[TICKETID: <id>]` to the item title, immediately after the row ID and before the body text. Example: `- F-001 [TICKETID: TICKET-123] — <summary> (path:line)`.
216
- - **Section headers in the final report** (e.g. `### 4.5.3 Recommended Option`): append `[TICKETID: <id>]` to the header when the section is scoped to a specific ticket. Headers that span all tickets in the run omit the tag.
216
+ - **Section headers in the final report** (e.g. `### 5.5.3 Recommended Option`): append `[TICKETID: <id>]` to the header when the section is scoped to a specific ticket. Headers that span all tickets in the run omit the tag.
217
217
 
218
218
  Ticket ID fill rule (in order):
219
219
 
@@ -42,128 +42,34 @@ approved: {{ frontmatter.approved | yaml_scalar }}
42
42
  | Approval Required? | `{% if verdictCard.approvalRequired %}yes — {{ t("verdictCard.approvalRequiredSuffix") }}{% else %}no{% endif %}` |
43
43
  | Next Step | {{ verdictCard.nextStep }} |
44
44
 
45
- {% if clarificationCarryIn and clarificationCarryIn.sourceFile %}
46
- ## 0. Clarification Response Carried In From Previous Run
47
-
48
- - Source file: `{{ clarificationCarryIn.sourceFile }}`
49
- - {{ t("sectionIntro.clarificationCarryIn") }}
50
-
51
- {% endif %}
52
- ## Summary of the Problem or Verification Target
53
-
54
- {{ t("sectionIntro.ticketCoverage") }}
45
+ ## 1. Clarification Items
55
46
 
56
- | {{ t("columns.recordMeta") }} | {{ t("columns.summary") }} |
57
- |--------|------------|
58
- {% for row in summary -%}
59
- | **{{ row.id }}**<br>Ticket: `{{ row.ticketId }}`<br>{{ t("columns.source") }}: {{ row.source }} | {{ row.summary }} |
60
- {% endfor %}
61
-
62
- {% if ticketCoverage.omit %}
63
- {# Ticket Coverage omitted entirely — release-handoff / final-verification #}
64
- {%- else %}
65
- ## Ticket Coverage
66
-
67
- {{ t("ticketCoverage.intro") }}
47
+ {{ t("sectionIntro.clarificationItems") }}
68
48
 
69
- {% if ticketCoverage.singleTicket -%}
70
- - Single ticket run: {{ ticketCoverage.singleTicket }}
49
+ {% if clarificationItems | length == 0 -%}
50
+ {{ t("emptyState.noClarification") }}
71
51
  {%- else %}
72
- | Ticket ID | {{ t("ticketCoverage.columnSections") }} | {{ t("ticketCoverage.columnRelatedIds") }} |
73
- |-----------|-----------|---------------|
74
- {% for row in ticketCoverage.rows -%}
75
- | `{{ row.ticketId }}` | `{{ row.sections }}` | `{{ row.relatedIds }}` |
76
- {% endfor %}
77
- {%- endif %}
78
-
79
- {% endif %}
80
- ## Execution Status by Agent
81
-
82
- {{ t("sectionIntro.executionStatus") }}
83
-
84
- | {{ t("columns.recordMeta") }} | Summary of Key Findings |
85
- |--------|-------------------------|
86
- {% for row in executionStatus -%}
87
- | **{{ row.agent }}**<br>Role: {{ row.role }}<br>Model: {{ row.model }}<br>Status: {{ row.status }}<br>{{ t("columns.rawTokens") }}: {{ row.totalTokens | format_int }}{% if row.cliTotalTokens %} (CLI: {{ row.cliTotalTokens | format_int }}){% endif %}<br>{{ t("columns.billableTokens") }}: {{ row.billableTokens | format_int }}<br>{{ t("columns.cost") }}: {{ row.costUsd | format_usd }}{% if row.cliCostUsd %} (+ CLI {{ row.cliCostUsd | format_usd }}){% endif %}<br>Duration: {{ row.durationMs | format_duration_ms }} | {{ row.summary }} |
88
- {% endfor %}
89
-
90
- ## {{ t("tokenSummary.heading") }}
91
-
92
- | {{ t("tokenSummary.tableHeaderItem") }} | {{ t("columns.rawTokens") }} | {{ t("columns.billableTokensInputEquiv") }} | {{ t("columns.cost") }} |
93
- |------|-----------|------------------------|------------|
94
- | {{ t("tokenSummary.rowLead") }} | `{{ tokenUsage.lead.totalTokens | format_int }}` | `{{ tokenUsage.lead.billableTokens | format_int }}` | `{{ tokenUsage.lead.costUsd | format_usd }}` |
95
- | {{ t("tokenSummary.rowWorkerTotal") }} | `{{ tokenUsage.worker.totalTokens | format_int }}` | `{{ tokenUsage.worker.billableTokens | format_int }}` | `{{ tokenUsage.worker.costUsd | format_usd }}` |
96
- | {{ t("tokenSummary.rowGrandTotal") }} | **`{{ tokenUsage.grand.totalTokens | format_int }}`** | **`{{ tokenUsage.grand.billableTokens | format_int }}`** | **`{{ tokenUsage.grand.costUsd | format_usd }}`** |
97
- {% if tokenUsage.cli and tokenUsage.cli.costUsd is not none and tokenUsage.cli.costUsd > 0 -%}
98
- | {{ t("tokenSummary.rowCliExtra") }} | | | `{{ tokenUsage.cli.costUsd | format_usd }}` |
99
- {% endif %}
100
-
101
- {# At Phase 6 numeric cells are null and render as `--`.
102
- Phase 7's okstra-token-usage.py populates tokenUsage in data.json then
103
- re-invokes this renderer to produce the final markdown. #}
104
-
105
- ## 1. Cross Verification Results
106
-
107
- {% if crossVerification.roundHistory and not crossVerification.roundHistory.disabled -%}
108
- ### 1.0 Round History
109
-
110
- {% if crossVerification.roundHistory.rounds | length > 1 -%}
111
- | Round | inputQueueSize | resolvedCount | carriedForwardCount | dispatches (worker:status:durationMs) | skippedWorkers (worker:reason) |
112
- |-------|----------------|---------------|----------------------|----------------------------------------|---------------------------------|
113
- {% for row in crossVerification.roundHistory.rounds -%}
114
- | {{ row.round }} | {{ row.inputQueueSize }} | {{ row.resolvedCount }} | {{ row.carriedForwardCount }} | {{ row.dispatches }} | {{ row.skippedWorkers }} |
115
- {% endfor %}
116
-
117
- - `round2SkippedReason`: `{{ crossVerification.roundHistory.round2SkippedReason }}` ← {{ t("roundHistory.round2SkippedReasonNote") }}
118
- {% elif crossVerification.roundHistory.rounds | length == 1 -%}
119
- {% set r = crossVerification.roundHistory.rounds[0] -%}
120
- - {{ t("roundHistory.singleRoundPrefix") }} resolved={{ r.resolvedCount }}, carriedForward={{ r.carriedForwardCount }}, round2SkippedReason=`{{ crossVerification.roundHistory.round2SkippedReason }}`
121
- {% else -%}
122
- - {{ t("roundHistory.noRoundsNote") }} round2SkippedReason=`{{ crossVerification.roundHistory.round2SkippedReason }}`
123
- {% endif %}
52
+ {{ t("clarification.fillAndRerun") }}
124
53
 
125
- {% endif %}
126
- ### 1.1 Consensus
54
+ - Claude Code: `/okstra-run resume-clarification task-key={{ header.taskKey }}`
55
+ - {{ t("clarification.separateTerminalLabel") }}: `scripts/okstra.sh --resume-clarification --task-key {{ header.taskKey }}`
127
56
 
128
- {% if crossVerification.consensus | length == 0 -%}
129
- {{ t("emptyState.consensusItems") }}
130
- {%- else %}
131
- | {{ t("columns.recordMeta") }} | Statement | Evidence (path:line / log / worker report) |
132
- |--------|-----------|---------------------------------------------|
133
- {% for row in crossVerification.consensus -%}
134
- | **{{ row.id }}**<br>Ticket: `{{ row.ticketId }}`<br>Source items: {{ row.sourceItems | join(', ') }} | {{ row.statement }} | {{ row.evidence }} |
57
+ | ID | Ticket ID | Kind | Statement | Expected form | Blocks | Status | User input |
58
+ |----|-----------|------|-----------|---------------|--------|--------|-----------|
59
+ {% for row in clarificationItems -%}
60
+ | {{ row.id }} | `{{ row.ticketId }}` | `{{ row.kind }}` | {{ row.statement }} | {{ row.expectedForm }} | `{{ row.blocks }}` | {{ row.status }} | {{ row.userInput or '' }} |
135
61
  {% endfor %}
136
- {%- endif %}
137
-
138
- {{ t("sectionIntro.sourceItemsRule") }}
139
62
 
140
- ### 1.2 Differences
63
+ {{ t("clarification.columnGuide") }}
141
64
 
142
- {% if crossVerification.differences | length == 0 -%}
143
- {{ t("emptyState.differences") }}
144
- {%- else %}
145
- | {{ t("columns.recordMeta") }} | Disagreement | Evidence |
146
- |--------|--------------|----------|
147
- {% for row in crossVerification.differences -%}
148
- | **{{ row.id }}**<br>Ticket: `{{ row.ticketId }}`<br>Workers: {% for w in row.workersPosition %}{{ w.worker }}:{{ w.itemId }} ({{ w.position }}){% if not loop.last %} / {% endif %}{% endfor %} | {{ row.disagreement }} | {{ row.evidence }} |
149
- {% endfor %}
65
+ - **`Kind`** `{material, decision, data-point}`.
66
+ - **`Blocks`** ∈ `{approval, next-phase, none}`.
67
+ - **`Status`** ∈ `{open, answered, resolved, obsolete}`.
150
68
  {%- endif %}
151
69
 
152
- ## 2. Final Verdict
70
+ ## 2. Evidence and Detailed Analysis
153
71
 
154
- {{ t("finalVerdict.intro") }}
155
-
156
- | {{ t("verdictCard.tableHeaderLabel") }} | {{ t("verdictCard.tableHeaderValue") }} |
157
- |------|----|
158
- | Final Conclusion | {{ finalVerdict.finalConclusion }} |
159
- | Verdict Token | `{{ finalVerdict.verdictToken }}` |
160
- | Direction | `{{ finalVerdict.direction }}` |
161
- | {{ t("verdictCard.rationaleLabel") }} | {{ finalVerdict.rationaleRowIds | join(', ') }} |
162
- | {{ t("verdictCard.nextStepLabel") }} | {{ finalVerdict.nextStep }} |
163
-
164
- ## 3. Evidence and Detailed Analysis
165
-
166
- ### 3.1 Primary Evidence
72
+ ### 2.1 Primary Evidence
167
73
 
168
74
  {% if evidence.primary | length == 0 -%}
169
75
  {{ t("emptyState.primaryEvidence") }}
@@ -177,7 +83,7 @@ approved: {{ frontmatter.approved | yaml_scalar }}
177
83
 
178
84
  {{ t("evidence.sourceItemsColumnNote") }}
179
85
 
180
- ### 3.2 Secondary Evidence or Alternate Interpretations
86
+ ### 2.2 Secondary Evidence or Alternate Interpretations
181
87
 
182
88
  {% if not evidence.secondary or evidence.secondary | length == 0 -%}
183
89
  {{ t("emptyState.secondaryEvidence") }}
@@ -189,7 +95,36 @@ approved: {{ frontmatter.approved | yaml_scalar }}
189
95
  {% endfor %}
190
96
  {%- endif %}
191
97
 
192
- ## 4. Missing Information and Risks
98
+ ## 3. Recommended Next Steps
99
+
100
+ {% if recommendedNextSteps | length == 0 -%}
101
+ - No further action required. Final verdict in section 7 stands.
102
+ {%- else %}
103
+ {% for step in recommendedNextSteps -%}
104
+ {{ loop.index }}. {{ step.text }}
105
+ {% if step.commands -%}
106
+ {% for cmd in step.commands %}
107
+ - {{ cmd.label }}:
108
+ - Claude Code: `{{ cmd.claudeCode }}`
109
+ - {{ t("clarification.separateTerminalLabel") }}: `{{ cmd.terminal }}`
110
+ {% endfor -%}
111
+ {%- endif %}
112
+ {% endfor %}
113
+ {%- endif %}
114
+
115
+ ## 4. Follow-up Tasks{% if t("followUpTasks.headingAside") != "Follow-up Tasks" %} ({{ t("followUpTasks.headingAside") }}){% endif %}
116
+
117
+ {% if followUpTasks | length == 0 -%}
118
+ {{ t("emptyState.noFollowUp") }}
119
+ {%- else %}
120
+ | {{ t("columns.recordMeta") }} | Title | Scope (files/areas) | Reason / Why deferred |
121
+ |--------|-------|---------------------|------------------------|
122
+ {% for row in followUpTasks -%}
123
+ | **{{ row.id }}**<br>Ticket: `{{ row.ticketId }}`<br>Origin: `{{ row.origin }}`<br>New Task ID: `{{ row.newTaskId }}`<br>Type: `{{ row.suggestedTaskType }}`<br>Priority: `{{ row.priority }}`<br>Auto-spawn: `{{ row.autoSpawn }}` | {{ row.title }} | {{ row.scope }} | {{ row.reason }} |
124
+ {% endfor %}
125
+ {%- endif %}
126
+
127
+ ## 5. Missing Information and Risks
193
128
 
194
129
  {% if missingInformation | length == 0 -%}
195
130
  {{ t("emptyState.risks") }}
@@ -202,9 +137,9 @@ approved: {{ frontmatter.approved | yaml_scalar }}
202
137
  {%- endif %}
203
138
 
204
139
  {% if header.taskType == 'implementation-planning' %}
205
- ## 4.5 Implementation Plan Deliverables
140
+ ## 5.5 Implementation Plan Deliverables
206
141
 
207
- ### 4.5.1 Option Candidates{% if t("sectionAside.optionCandidates") != "Option Candidates" %} ({{ t("sectionAside.optionCandidates") }}){% endif %}
142
+ ### 5.5.1 Option Candidates{% if t("sectionAside.optionCandidates") != "Option Candidates" %} ({{ t("sectionAside.optionCandidates") }}){% endif %}
208
143
 
209
144
  {% for opt in implementationPlanning.optionCandidates %}
210
145
  **{{ opt.name }}**
@@ -222,7 +157,7 @@ approved: {{ frontmatter.approved | yaml_scalar }}
222
157
 
223
158
  {% endfor %}
224
159
 
225
- ### 4.5.2 Trade-off Matrix{% if t("sectionAside.tradeOffMatrix") != "Trade-off Matrix" %} ({{ t("sectionAside.tradeOffMatrix") }}){% endif %}
160
+ ### 5.5.2 Trade-off Matrix{% if t("sectionAside.tradeOffMatrix") != "Trade-off Matrix" %} ({{ t("sectionAside.tradeOffMatrix") }}){% endif %}
226
161
 
227
162
  | Option | Complexity | Risk | Reversibility | Test Coverage Cost | Rollout Cost |
228
163
  |--------|-----------|------|---------------|--------------------|--------------|
@@ -230,7 +165,7 @@ approved: {{ frontmatter.approved | yaml_scalar }}
230
165
  | {{ row.option }} | {{ row.complexity }} | {{ row.risk }} | {{ row.reversibility }} | {{ row.testCoverageCost }} | {{ row.rolloutCost }} |
231
166
  {% endfor %}
232
167
 
233
- ### 4.5.3 Recommended Option{% if t("sectionAside.recommendedOption") != "Recommended Option" %} ({{ t("sectionAside.recommendedOption") }}){% endif %}
168
+ ### 5.5.3 Recommended Option{% if t("sectionAside.recommendedOption") != "Recommended Option" %} ({{ t("sectionAside.recommendedOption") }}){% endif %}
234
169
 
235
170
  | {{ t("implementationPlanning.recommendedTableHeaderLabel") }} | {{ t("implementationPlanning.recommendedTableHeaderValue") }} |
236
171
  |------|----|
@@ -239,7 +174,7 @@ approved: {{ frontmatter.approved | yaml_scalar }}
239
174
  | {{ t("implementationPlanning.rationaleLabel") }} | {{ implementationPlanning.recommendedOption.rationale }} |
240
175
  | {{ t("implementationPlanning.rejectedSummaryLabel") }} | {{ implementationPlanning.recommendedOption.rejectedSummary }} |
241
176
 
242
- ### 4.5.4 Stepwise Execution Order{% if t("sectionAside.stepwiseExecutionOrder") != "Stepwise Execution Order" %} ({{ t("sectionAside.stepwiseExecutionOrder") }}){% endif %}
177
+ ### 5.5.4 Stepwise Execution Order{% if t("sectionAside.stepwiseExecutionOrder") != "Stepwise Execution Order" %} ({{ t("sectionAside.stepwiseExecutionOrder") }}){% endif %}
243
178
 
244
179
  | Step | Ticket ID | Action (≤ 5min) | Files | Command / Test | Expected outcome |
245
180
  |------|-----------|------------------|-------|----------------|-------------------|
@@ -247,7 +182,7 @@ approved: {{ frontmatter.approved | yaml_scalar }}
247
182
  | {{ row.step }} | `{{ row.ticketId }}` | {{ row.action }} | `{{ row.files }}` | `{{ row.commandOrTest }}` | {{ row.expectedOutcome }} |
248
183
  {% endfor %}
249
184
 
250
- ### 4.5.5 Dependency / Migration Risk{% if t("sectionAside.dependencyRisk") != "Dependency / Migration Risk" %} ({{ t("sectionAside.dependencyRisk") }}){% endif %}
185
+ ### 5.5.5 Dependency / Migration Risk{% if t("sectionAside.dependencyRisk") != "Dependency / Migration Risk" %} ({{ t("sectionAside.dependencyRisk") }}){% endif %}
251
186
 
252
187
  {% if implementationPlanning.dependencyMigrationRisk | length == 0 -%}
253
188
  {{ t("emptyState.dependencyRisk") }}
@@ -259,7 +194,7 @@ approved: {{ frontmatter.approved | yaml_scalar }}
259
194
  {% endfor %}
260
195
  {%- endif %}
261
196
 
262
- ### 4.5.6 Validation Checklist{% if t("sectionAside.validationChecklist") != "Validation Checklist" %} ({{ t("sectionAside.validationChecklist") }}){% endif %}
197
+ ### 5.5.6 Validation Checklist{% if t("sectionAside.validationChecklist") != "Validation Checklist" %} ({{ t("sectionAside.validationChecklist") }}){% endif %}
263
198
 
264
199
  | Phase | Ticket ID | Check | Command / Observation | Expected outcome |
265
200
  |-------|-----------|-------|------------------------|-------------------|
@@ -267,7 +202,7 @@ approved: {{ frontmatter.approved | yaml_scalar }}
267
202
  | {{ row.phase }} | `{{ row.ticketId }}` | {{ row.check }} | `{{ row.commandOrObservation }}` | {{ row.expectedOutcome }} |
268
203
  {% endfor %}
269
204
 
270
- ### 4.5.7 Rollback Strategy{% if t("sectionAside.rollbackStrategy") != "Rollback Strategy" %} ({{ t("sectionAside.rollbackStrategy") }}){% endif %}
205
+ ### 5.5.7 Rollback Strategy{% if t("sectionAside.rollbackStrategy") != "Rollback Strategy" %} ({{ t("sectionAside.rollbackStrategy") }}){% endif %}
271
206
 
272
207
  | ID | Step | Action | Trigger signal | {{ t("columns.checkMethod") }} |
273
208
  |----|------|--------|----------------|-----------|
@@ -275,7 +210,7 @@ approved: {{ frontmatter.approved | yaml_scalar }}
275
210
  | {{ row.id }} | {{ row.step }} | `{{ row.action }}` | {{ row.triggerSignal }} | `{{ row.verificationMethod }}` |
276
211
  {% endfor %}
277
212
 
278
- ### 4.5.9 Plan Body Verification{% if t("sectionAside.planBodyVerification") != "Plan Body Verification" %} ({{ t("sectionAside.planBodyVerification") }}){% endif %}
213
+ ### 5.5.9 Plan Body Verification{% if t("sectionAside.planBodyVerification") != "Plan Body Verification" %} ({{ t("sectionAside.planBodyVerification") }}){% endif %}
279
214
 
280
215
  {{ t("sectionIntro.planBodyVerification") }}
281
216
 
@@ -302,17 +237,17 @@ approved: {{ frontmatter.approved | yaml_scalar }}
302
237
 
303
238
  {% endif %}
304
239
  {% if header.taskType == 'release-handoff' %}
305
- ## 4.6 Release Handoff Deliverables
240
+ ## 5.6 Release Handoff Deliverables
306
241
 
307
242
  {{ t("releaseHandoff.auditNote") }}
308
243
 
309
- ### 4.6.1 Source Verification Report
244
+ ### 5.6.1 Source Verification Report
310
245
 
311
246
  - Path (project-relative): `{{ releaseHandoff.sourceVerificationReport.path }}`
312
- - Quoted `Verdict Token` row from that report's `## 2.` table:
247
+ - Quoted `Verdict Token` row from that report's `## 7.` table:
313
248
  > {{ releaseHandoff.sourceVerificationReport.verdictTokenQuote }}
314
249
 
315
- ### 4.6.2 Feature Branch & Working-Tree State{% if t("releaseHandoff.branchStateAside") != "captured at run start" %} ({{ t("releaseHandoff.branchStateAside") }}){% endif %}
250
+ ### 5.6.2 Feature Branch & Working-Tree State{% if t("releaseHandoff.branchStateAside") != "captured at run start" %} ({{ t("releaseHandoff.branchStateAside") }}){% endif %}
316
251
 
317
252
  - Feature branch: `{{ releaseHandoff.featureBranchState.branchName }}`
318
253
  - {{ t("releaseHandoff.gitStatusShortLabel") }}:
@@ -321,7 +256,7 @@ approved: {{ frontmatter.approved | yaml_scalar }}
321
256
  ```
322
257
  - {{ t("releaseHandoff.existingPrLabel") }}: `{{ releaseHandoff.featureBranchState.existingPrUrl or '(none)' }}`
323
258
 
324
- ### 4.6.3 User Selections{% if t("releaseHandoff.userSelectionsAside") != "menu response log" %} ({{ t("releaseHandoff.userSelectionsAside") }}){% endif %}
259
+ ### 5.6.3 User Selections{% if t("releaseHandoff.userSelectionsAside") != "menu response log" %} ({{ t("releaseHandoff.userSelectionsAside") }}){% endif %}
325
260
 
326
261
  | {{ t("releaseHandoff.questionsTableHeader.questionId") }} | {{ t("releaseHandoff.questionsTableHeader.questionBody") }} | {{ t("releaseHandoff.questionsTableHeader.userResponse") }} | {{ t("releaseHandoff.questionsTableHeader.allowedOptions") }} |
327
262
  |---------|-----------|--------------------|--------------------|
@@ -329,7 +264,7 @@ approved: {{ frontmatter.approved | yaml_scalar }}
329
264
  | H2 | {{ t("releaseHandoff.h2Body") }} | `{{ releaseHandoff.userSelections.h2 or t("releaseHandoff.h2DefaultLabel") }}` | {{ t("releaseHandoff.h2OptionsLabel") }} |
330
265
  | H3 | {{ t("releaseHandoff.h3Body") }} | `{{ releaseHandoff.userSelections.h3 }}` | `use as-is` / `edit then proceed` / `cancel` |
331
266
 
332
- ### 4.6.4 Executed Commands
267
+ ### 5.6.4 Executed Commands
333
268
 
334
269
  {% if releaseHandoff.executedCommands | length == 0 -%}
335
270
  - {{ t("releaseHandoff.noMutationNote") }}
@@ -341,7 +276,7 @@ approved: {{ frontmatter.approved | yaml_scalar }}
341
276
  {% endfor %}
342
277
  {%- endif %}
343
278
 
344
- ### 4.6.5 Commit List
279
+ ### 5.6.5 Commit List
345
280
 
346
281
  {% if releaseHandoff.commitList.empty -%}
347
282
  - No implementation commits found; release-handoff is blocked.
@@ -353,7 +288,7 @@ approved: {{ frontmatter.approved | yaml_scalar }}
353
288
  {% endfor %}
354
289
  {%- endif %}
355
290
 
356
- ### 4.6.6 Merge Conflict Probe
291
+ ### 5.6.6 Merge Conflict Probe
357
292
 
358
293
  {% if releaseHandoff.mergeConflictProbe.kind == 'not-run' -%}
359
294
  - Not run (user picked local only or skip).
@@ -363,7 +298,7 @@ approved: {{ frontmatter.approved | yaml_scalar }}
363
298
  - Conflicts detected against {{ releaseHandoff.mergeConflictProbe.baseBranch }} at {{ releaseHandoff.mergeConflictProbe.baseSha }}; user chose {{ releaseHandoff.mergeConflictProbe.userChoice }}. Conflicting paths: {{ releaseHandoff.mergeConflictProbe.conflictingPaths | join(', ') }}.
364
299
  {%- endif %}
365
300
 
366
- ### 4.6.7 Pull Request Outcome
301
+ ### 5.6.7 Pull Request Outcome
367
302
 
368
303
  {% if releaseHandoff.pullRequestOutcome.kind == 'no-action' -%}
369
304
  - No PR action requested.
@@ -375,15 +310,15 @@ approved: {{ frontmatter.approved | yaml_scalar }}
375
310
  - PR creation skipped: {{ releaseHandoff.pullRequestOutcome.reason }}
376
311
  {%- endif %}
377
312
 
378
- ### 4.6.8 Routing Recommendation
313
+ ### 5.6.8 Routing Recommendation
379
314
 
380
315
  {{ releaseHandoff.routingRecommendation }}
381
316
 
382
317
  {% endif %}
383
318
  {% if header.taskType == 'implementation' %}
384
- ## 4.7 Implementation Deliverables
319
+ ## 5.7 Implementation Deliverables
385
320
 
386
- ### 4.7.1 Approved Plan Reference
321
+ ### 5.7.1 Approved Plan Reference
387
322
 
388
323
  - Plan file (project-relative): `{{ implementation.approvedPlanReference.planFile }}`
389
324
  - Approval evidence:
@@ -391,7 +326,7 @@ approved: {{ frontmatter.approved | yaml_scalar }}
391
326
  - {{ t("executionMeta.runExecutorWorktreePath") }}: `{{ implementation.approvedPlanReference.executorWorktreePath }}`
392
327
  - {{ t("executionMeta.runBaseRef") }}: `{{ implementation.approvedPlanReference.baseRefSha }}`
393
328
 
394
- ### 4.7.2 Commit List
329
+ ### 5.7.2 Commit List
395
330
 
396
331
  {% if implementation.commitList | length == 0 -%}
397
332
  - No implementation commits produced; routing recommendation must be back to implementation-planning.
@@ -403,7 +338,7 @@ approved: {{ frontmatter.approved | yaml_scalar }}
403
338
  {% endfor %}
404
339
  {%- endif %}
405
340
 
406
- ### 4.7.3 Diff Summary
341
+ ### 5.7.3 Diff Summary
407
342
 
408
343
  ```
409
344
  {{ implementation.diffSummary.rawStat }}
@@ -415,7 +350,7 @@ approved: {{ frontmatter.approved | yaml_scalar }}
415
350
  | `{{ row.file }}` | {{ row.action }} | `{{ row.lines }}` | {{ row.planStep }} |
416
351
  {% endfor %}
417
352
 
418
- ### 4.7.4 Out-of-plan Edits
353
+ ### 5.7.4 Out-of-plan Edits
419
354
 
420
355
  {% if implementation.outOfPlanEdits | length == 0 -%}
421
356
  {{ t("emptyState.outOfPlanEdits") }}
@@ -427,7 +362,7 @@ approved: {{ frontmatter.approved | yaml_scalar }}
427
362
  {% endfor %}
428
363
  {%- endif %}
429
364
 
430
- ### 4.7.5 Validation Evidence
365
+ ### 5.7.5 Validation Evidence
431
366
 
432
367
  | Phase | Command | Exit code | Output tail | TDD evidence |
433
368
  |-------|---------|-----------|-------------|--------------|
@@ -435,7 +370,7 @@ approved: {{ frontmatter.approved | yaml_scalar }}
435
370
  | {{ row.phase }} | `{{ row.command }}` | `{{ row.exitCode }}` | {{ row.outputTail }} | {{ row.tddEvidence or '--' }} |
436
371
  {% endfor %}
437
372
 
438
- ### 4.7.6 Verifier Results
373
+ ### 5.7.6 Verifier Results
439
374
 
440
375
  {% for block in implementation.verifierResults %}
441
376
  - **{{ block.verifier }}** — Verdict: `{{ block.verdict }}`
@@ -446,7 +381,7 @@ approved: {{ frontmatter.approved | yaml_scalar }}
446
381
  - Discrepancy: {{ block.discrepancy or t("emptyState.discrepancy") }}
447
382
  {% endfor %}
448
383
 
449
- ### 4.7.7 Rollback Verification
384
+ ### 5.7.7 Rollback Verification
450
385
 
451
386
  | Category | Rollback command | Verification | Result |
452
387
  |----------|-------------------|---------------|--------|
@@ -454,15 +389,15 @@ approved: {{ frontmatter.approved | yaml_scalar }}
454
389
  | {{ row.category }} | `{{ row.rollbackCommand }}` | {{ row.verification }} | `{{ row.result }}` |
455
390
  {% endfor %}
456
391
 
457
- ### 4.7.8 Routing Recommendation
392
+ ### 5.7.8 Routing Recommendation
458
393
 
459
394
  {{ implementation.routingRecommendation }}
460
395
 
461
396
  {% endif %}
462
397
  {% if header.taskType == 'final-verification' %}
463
- ## 4.8 Final Verification Deliverables
398
+ ## 5.8 Final Verification Deliverables
464
399
 
465
- ### 4.8.1 Source Implementation Report
400
+ ### 5.8.1 Source Implementation Report
466
401
 
467
402
  - Path (project-relative): `{{ finalVerification.sourceImplementationReport.path }}`
468
403
  - {{ t("evidenceMeta.commitListSummary") }}:
@@ -481,7 +416,7 @@ approved: {{ frontmatter.approved | yaml_scalar }}
481
416
  {{ finalVerification.sourceImplementationReport.gitDiffStat }}
482
417
  ```
483
418
 
484
- ### 4.8.2 Acceptance Blockers
419
+ ### 5.8.2 Acceptance Blockers
485
420
 
486
421
  {% if finalVerification.acceptanceBlockers | length == 0 -%}
487
422
  - No acceptance blockers found.
@@ -493,7 +428,7 @@ approved: {{ frontmatter.approved | yaml_scalar }}
493
428
  {% endfor %}
494
429
  {%- endif %}
495
430
 
496
- ### 4.8.3 Residual Risk
431
+ ### 5.8.3 Residual Risk
497
432
 
498
433
  {% if finalVerification.residualRisk | length == 0 -%}
499
434
  {{ t("emptyState.lingeringRisks") }}
@@ -505,7 +440,7 @@ approved: {{ frontmatter.approved | yaml_scalar }}
505
440
  {% endfor %}
506
441
  {%- endif %}
507
442
 
508
- ### 4.8.4 Validation Evidence{% if t("finalVerification.validationEvidenceAside") != "requirements coverage" %} ({{ t("finalVerification.validationEvidenceAside") }}){% endif %}
443
+ ### 5.8.4 Validation Evidence{% if t("finalVerification.validationEvidenceAside") != "requirements coverage" %} ({{ t("finalVerification.validationEvidenceAside") }}){% endif %}
509
444
 
510
445
  | ID | {{ t("finalVerification.columnRequirement") }} | Artifact | Status |
511
446
  |----|--------------------------------|----------|--------|
@@ -513,7 +448,7 @@ approved: {{ frontmatter.approved | yaml_scalar }}
513
448
  | {{ row.id }} | {{ row.requirement }} | `{{ row.artifact }}` | {{ row.status }} |
514
449
  {% endfor %}
515
450
 
516
- ### 4.8.5 Read-only Command Log
451
+ ### 5.8.5 Read-only Command Log
517
452
 
518
453
  | # | Tier | Command (verbatim) | Status | Exit code | Output tail |
519
454
  |---|------|---------------------|--------|-----------|-------------|
@@ -521,7 +456,7 @@ approved: {{ frontmatter.approved | yaml_scalar }}
521
456
  | {{ row.number }} | {{ row.tier }} | `{{ row.command }}` | `{{ row.status }}` | {{ row.exitCode if row.exitCode is not none else '—' }} | {{ row.rejectionReason if row.status == 'rejected' else row.outputTail }} |
522
457
  {% endfor %}
523
458
 
524
- ### 4.8.6 Conditional Acceptance Conditions
459
+ ### 5.8.6 Conditional Acceptance Conditions
525
460
 
526
461
  {% if not finalVerdict.conditionalAcceptanceConditions -%}
527
462
  - Not applicable (verdict is not `conditional-accept`).
@@ -533,13 +468,13 @@ approved: {{ frontmatter.approved | yaml_scalar }}
533
468
  {% endfor %}
534
469
  {%- endif %}
535
470
 
536
- ### 4.8.7 Routing Recommendation
471
+ ### 5.8.7 Routing Recommendation
537
472
 
538
473
  {{ finalVerification.routingRecommendation }}
539
474
 
540
475
  {% endif %}
541
476
  {% if header.taskType == 'improvement-discovery' %}
542
- ## 4.9 Improvement Candidates
477
+ ## 5.9 Improvement Candidates
543
478
 
544
479
  > Lens whitelist and column schema enforced by `validators/validate-improvement-report.py`. Row count is bounded by the brief's `candidate-cap` (default 8, absolute max 12).
545
480
 
@@ -547,56 +482,121 @@ approved: {{ frontmatter.approved | yaml_scalar }}
547
482
  |---------|------|-------|-------|----------|--------|-----------|----------------|------------------------|----------|
548
483
 
549
484
  {% endif %}
550
- ## 5. Clarification Items
551
-
552
- {{ t("sectionIntro.clarificationItems") }}
485
+ ## 6. Cross Verification Results
553
486
 
554
- {% if clarificationItems | length == 0 -%}
555
- {{ t("emptyState.noClarification") }}
556
- {%- else %}
557
- {{ t("clarification.fillAndRerun") }}
558
-
559
- - Claude Code: `/okstra-run resume-clarification task-key={{ header.taskKey }}`
560
- - {{ t("clarification.separateTerminalLabel") }}: `scripts/okstra.sh --resume-clarification --task-key {{ header.taskKey }}`
487
+ {% if crossVerification.roundHistory and not crossVerification.roundHistory.disabled -%}
488
+ ### 6.0 Round History
561
489
 
562
- | ID | Ticket ID | Kind | Statement | Expected form | Blocks | Status | User input |
563
- |----|-----------|------|-----------|---------------|--------|--------|-----------|
564
- {% for row in clarificationItems -%}
565
- | {{ row.id }} | `{{ row.ticketId }}` | `{{ row.kind }}` | {{ row.statement }} | {{ row.expectedForm }} | `{{ row.blocks }}` | {{ row.status }} | {{ row.userInput or '' }} |
490
+ {% if crossVerification.roundHistory.rounds | length > 1 -%}
491
+ | Round | inputQueueSize | resolvedCount | carriedForwardCount | dispatches (worker:status:durationMs) | skippedWorkers (worker:reason) |
492
+ |-------|----------------|---------------|----------------------|----------------------------------------|---------------------------------|
493
+ {% for row in crossVerification.roundHistory.rounds -%}
494
+ | {{ row.round }} | {{ row.inputQueueSize }} | {{ row.resolvedCount }} | {{ row.carriedForwardCount }} | {{ row.dispatches }} | {{ row.skippedWorkers }} |
566
495
  {% endfor %}
567
496
 
568
- {{ t("clarification.columnGuide") }}
497
+ - `round2SkippedReason`: `{{ crossVerification.roundHistory.round2SkippedReason }}` ← {{ t("roundHistory.round2SkippedReasonNote") }}
498
+ {% elif crossVerification.roundHistory.rounds | length == 1 -%}
499
+ {% set r = crossVerification.roundHistory.rounds[0] -%}
500
+ - {{ t("roundHistory.singleRoundPrefix") }} resolved={{ r.resolvedCount }}, carriedForward={{ r.carriedForwardCount }}, round2SkippedReason=`{{ crossVerification.roundHistory.round2SkippedReason }}`
501
+ {% else -%}
502
+ - {{ t("roundHistory.noRoundsNote") }} round2SkippedReason=`{{ crossVerification.roundHistory.round2SkippedReason }}`
503
+ {% endif %}
569
504
 
570
- - **`Kind`** ∈ `{material, decision, data-point}`.
571
- - **`Blocks`** ∈ `{approval, next-phase, none}`.
572
- - **`Status`** ∈ `{open, answered, resolved, obsolete}`.
505
+ {% endif %}
506
+ ### 6.1 Consensus
507
+
508
+ {% if crossVerification.consensus | length == 0 -%}
509
+ {{ t("emptyState.consensusItems") }}
510
+ {%- else %}
511
+ | {{ t("columns.recordMeta") }} | Statement | Evidence (path:line / log / worker report) |
512
+ |--------|-----------|---------------------------------------------|
513
+ {% for row in crossVerification.consensus -%}
514
+ | **{{ row.id }}**<br>Ticket: `{{ row.ticketId }}`<br>Source items: {{ row.sourceItems | join(', ') }} | {{ row.statement }} | {{ row.evidence }} |
515
+ {% endfor %}
573
516
  {%- endif %}
574
517
 
575
- ## 6. Recommended Next Steps
518
+ {{ t("sectionIntro.sourceItemsRule") }}
519
+
520
+ ### 6.2 Differences
576
521
 
577
- {% if recommendedNextSteps | length == 0 -%}
578
- - No further action required. Final verdict in section 2 stands.
522
+ {% if crossVerification.differences | length == 0 -%}
523
+ {{ t("emptyState.differences") }}
579
524
  {%- else %}
580
- {% for step in recommendedNextSteps -%}
581
- {{ loop.index }}. {{ step.text }}
582
- {% if step.commands -%}
583
- {% for cmd in step.commands %}
584
- - {{ cmd.label }}:
585
- - Claude Code: `{{ cmd.claudeCode }}`
586
- - {{ t("clarification.separateTerminalLabel") }}: `{{ cmd.terminal }}`
587
- {% endfor -%}
588
- {%- endif %}
525
+ | {{ t("columns.recordMeta") }} | Disagreement | Evidence |
526
+ |--------|--------------|----------|
527
+ {% for row in crossVerification.differences -%}
528
+ | **{{ row.id }}**<br>Ticket: `{{ row.ticketId }}`<br>Workers: {% for w in row.workersPosition %}{{ w.worker }}:{{ w.itemId }} ({{ w.position }}){% if not loop.last %} / {% endif %}{% endfor %} | {{ row.disagreement }} | {{ row.evidence }} |
589
529
  {% endfor %}
590
530
  {%- endif %}
591
531
 
592
- ## 7. Follow-up Tasks{% if t("followUpTasks.headingAside") != "Follow-up Tasks" %} ({{ t("followUpTasks.headingAside") }}){% endif %}
532
+ ## 7. Final Verdict
593
533
 
594
- {% if followUpTasks | length == 0 -%}
595
- {{ t("emptyState.noFollowUp") }}
534
+ {{ t("finalVerdict.intro") }}
535
+
536
+ | {{ t("verdictCard.tableHeaderLabel") }} | {{ t("verdictCard.tableHeaderValue") }} |
537
+ |------|----|
538
+ | Final Conclusion | {{ finalVerdict.finalConclusion }} |
539
+ | Verdict Token | `{{ finalVerdict.verdictToken }}` |
540
+ | Direction | `{{ finalVerdict.direction }}` |
541
+ | {{ t("verdictCard.rationaleLabel") }} | {{ finalVerdict.rationaleRowIds | join(', ') }} |
542
+ | {{ t("verdictCard.nextStepLabel") }} | {{ finalVerdict.nextStep }} |
543
+
544
+ {% if clarificationCarryIn and clarificationCarryIn.sourceFile %}
545
+ ## 0. Clarification Response Carried In From Previous Run
546
+
547
+ - Source file: `{{ clarificationCarryIn.sourceFile }}`
548
+ - {{ t("sectionIntro.clarificationCarryIn") }}
549
+
550
+ {% endif %}
551
+ ## Summary of the Problem or Verification Target
552
+
553
+ {{ t("sectionIntro.ticketCoverage") }}
554
+
555
+ | {{ t("columns.recordMeta") }} | {{ t("columns.summary") }} |
556
+ |--------|------------|
557
+ {% for row in summary -%}
558
+ | **{{ row.id }}**<br>Ticket: `{{ row.ticketId }}`<br>{{ t("columns.source") }}: {{ row.source }} | {{ row.summary }} |
559
+ {% endfor %}
560
+
561
+ {% if ticketCoverage.omit %}
562
+ {# Ticket Coverage omitted entirely — release-handoff / final-verification #}
596
563
  {%- else %}
597
- | {{ t("columns.recordMeta") }} | Title | Scope (files/areas) | Reason / Why deferred |
598
- |--------|-------|---------------------|------------------------|
599
- {% for row in followUpTasks -%}
600
- | **{{ row.id }}**<br>Ticket: `{{ row.ticketId }}`<br>Origin: `{{ row.origin }}`<br>New Task ID: `{{ row.newTaskId }}`<br>Type: `{{ row.suggestedTaskType }}`<br>Priority: `{{ row.priority }}`<br>Auto-spawn: `{{ row.autoSpawn }}` | {{ row.title }} | {{ row.scope }} | {{ row.reason }} |
564
+ ## Ticket Coverage
565
+
566
+ {{ t("ticketCoverage.intro") }}
567
+
568
+ {% if ticketCoverage.singleTicket -%}
569
+ - Single ticket run: {{ ticketCoverage.singleTicket }}
570
+ {%- else %}
571
+ | Ticket ID | {{ t("ticketCoverage.columnSections") }} | {{ t("ticketCoverage.columnRelatedIds") }} |
572
+ |-----------|-----------|---------------|
573
+ {% for row in ticketCoverage.rows -%}
574
+ | `{{ row.ticketId }}` | `{{ row.sections }}` | `{{ row.relatedIds }}` |
601
575
  {% endfor %}
602
576
  {%- endif %}
577
+
578
+ {% endif %}
579
+ ## Execution Status by Agent
580
+
581
+ {{ t("sectionIntro.executionStatus") }}
582
+
583
+ | {{ t("columns.recordMeta") }} | Summary of Key Findings |
584
+ |--------|-------------------------|
585
+ {% for row in executionStatus -%}
586
+ | **{{ row.agent }}**<br>Role: {{ row.role }}<br>Model: {{ row.model }}<br>Status: {{ row.status }}<br>{{ t("columns.rawTokens") }}: {{ row.totalTokens | format_int }}{% if row.cliTotalTokens %} (CLI: {{ row.cliTotalTokens | format_int }}){% endif %}<br>{{ t("columns.billableTokens") }}: {{ row.billableTokens | format_int }}<br>{{ t("columns.cost") }}: {{ row.costUsd | format_usd }}{% if row.cliCostUsd %} (+ CLI {{ row.cliCostUsd | format_usd }}){% endif %}<br>Duration: {{ row.durationMs | format_duration_ms }} | {{ row.summary }} |
587
+ {% endfor %}
588
+
589
+ ## {{ t("tokenSummary.heading") }}
590
+
591
+ | {{ t("tokenSummary.tableHeaderItem") }} | {{ t("columns.rawTokens") }} | {{ t("columns.billableTokensInputEquiv") }} | {{ t("columns.cost") }} |
592
+ |------|-----------|------------------------|------------|
593
+ | {{ t("tokenSummary.rowLead") }} | `{{ tokenUsage.lead.totalTokens | format_int }}` | `{{ tokenUsage.lead.billableTokens | format_int }}` | `{{ tokenUsage.lead.costUsd | format_usd }}` |
594
+ | {{ t("tokenSummary.rowWorkerTotal") }} | `{{ tokenUsage.worker.totalTokens | format_int }}` | `{{ tokenUsage.worker.billableTokens | format_int }}` | `{{ tokenUsage.worker.costUsd | format_usd }}` |
595
+ | {{ t("tokenSummary.rowGrandTotal") }} | **`{{ tokenUsage.grand.totalTokens | format_int }}`** | **`{{ tokenUsage.grand.billableTokens | format_int }}`** | **`{{ tokenUsage.grand.costUsd | format_usd }}`** |
596
+ {% if tokenUsage.cli and tokenUsage.cli.costUsd is not none and tokenUsage.cli.costUsd > 0 -%}
597
+ | {{ t("tokenSummary.rowCliExtra") }} | | | `{{ tokenUsage.cli.costUsd | format_usd }}` |
598
+ {% endif %}
599
+
600
+ {# At Phase 6 numeric cells are null and render as `--`.
601
+ Phase 7's okstra-token-usage.py populates tokenUsage in data.json then
602
+ re-invokes this renderer to produce the final markdown. #}