okstra 0.148.1 → 0.150.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 (78) hide show
  1. package/docs/architecture.md +1 -1
  2. package/docs/cli.md +1 -1
  3. package/docs/project-structure-overview.md +1 -1
  4. package/package.json +1 -1
  5. package/runtime/BUILD.json +2 -2
  6. package/runtime/agents/workers/translator-worker.md +67 -0
  7. package/runtime/bin/okstra-render-final-report.py +0 -11
  8. package/runtime/bin/okstra-render-report-views.py +20 -0
  9. package/runtime/bin/okstra-report-translate.py +158 -0
  10. package/runtime/prompts/lead/adapters/claude-code.md +1 -1
  11. package/runtime/prompts/lead/okstra-lead-contract.md +1 -0
  12. package/runtime/prompts/lead/report-writer.md +14 -13
  13. package/runtime/prompts/lead/team-contract.md +2 -2
  14. package/runtime/prompts/profiles/project-analysis.md +18 -0
  15. package/runtime/prompts/profiles/release-handoff.md +3 -0
  16. package/runtime/prompts/profiles/requirements-discovery.md +7 -0
  17. package/runtime/prompts/wizard/prompts.ko.json +17 -1
  18. package/runtime/python/okstra_ctl/analysis_inputs.py +24 -9
  19. package/runtime/python/okstra_ctl/analysis_packet.py +23 -1
  20. package/runtime/python/okstra_ctl/clarification_items.py +241 -44
  21. package/runtime/python/okstra_ctl/codex_dispatch.py +2 -1
  22. package/runtime/python/okstra_ctl/dispatch_core.py +2 -2
  23. package/runtime/python/okstra_ctl/dispatch_state.py +12 -1
  24. package/runtime/python/okstra_ctl/final_report_paths.py +22 -1
  25. package/runtime/python/okstra_ctl/i18n.py +12 -7
  26. package/runtime/python/okstra_ctl/render_final_report.py +18 -17
  27. package/runtime/python/okstra_ctl/report_html/common.py +77 -47
  28. package/runtime/python/okstra_ctl/report_html/filters.py +63 -30
  29. package/runtime/python/okstra_ctl/report_html/models.py +15 -0
  30. package/runtime/python/okstra_ctl/report_html/render.py +86 -5
  31. package/runtime/python/okstra_ctl/report_html/view_models/change_impact_analysis.py +14 -4
  32. package/runtime/python/okstra_ctl/report_html/view_models/error_analysis.py +3 -3
  33. package/runtime/python/okstra_ctl/report_html/view_models/feature_analysis.py +5 -3
  34. package/runtime/python/okstra_ctl/report_html/view_models/final_verification.py +2 -7
  35. package/runtime/python/okstra_ctl/report_html/view_models/implementation.py +5 -9
  36. package/runtime/python/okstra_ctl/report_html/view_models/implementation_planning.py +2 -5
  37. package/runtime/python/okstra_ctl/report_html/view_models/improvement_discovery.py +1 -2
  38. package/runtime/python/okstra_ctl/report_html/view_models/project_analysis.py +90 -4
  39. package/runtime/python/okstra_ctl/report_html/view_models/release_handoff.py +1 -8
  40. package/runtime/python/okstra_ctl/report_html/view_models/requirements_discovery.py +6 -4
  41. package/runtime/python/okstra_ctl/report_html/visualizations.py +146 -11
  42. package/runtime/python/okstra_ctl/report_translation.py +440 -0
  43. package/runtime/python/okstra_ctl/report_view_artifacts.py +5 -0
  44. package/runtime/python/okstra_ctl/report_views.py +23 -9
  45. package/runtime/python/okstra_ctl/run.py +1 -1
  46. package/runtime/python/okstra_ctl/time_report.py +2 -2
  47. package/runtime/python/okstra_ctl/usage_report.py +2 -2
  48. package/runtime/python/okstra_ctl/user_response.py +11 -6
  49. package/runtime/python/okstra_ctl/wizard.py +100 -25
  50. package/runtime/python/okstra_ctl/worker_liveness.py +130 -36
  51. package/runtime/schemas/final-report-v2.0.schema.json +229 -1
  52. package/runtime/templates/reports/final-report.template.md +55 -0
  53. package/runtime/templates/reports/html/assets/base.css +59 -9
  54. package/runtime/templates/reports/html/base.template.html +23 -44
  55. package/runtime/templates/reports/html/i18n/en.json +395 -0
  56. package/runtime/templates/reports/html/i18n/ko.json +395 -0
  57. package/runtime/templates/reports/html/macros/forms.html +20 -20
  58. package/runtime/templates/reports/html/macros/layout.html +18 -5
  59. package/runtime/templates/reports/html/macros/visualizations.html +7 -5
  60. package/runtime/templates/reports/html/tasks/change-impact-analysis.template.html +30 -15
  61. package/runtime/templates/reports/html/tasks/error-analysis.template.html +22 -15
  62. package/runtime/templates/reports/html/tasks/feature-analysis.template.html +35 -15
  63. package/runtime/templates/reports/html/tasks/final-verification.template.html +21 -14
  64. package/runtime/templates/reports/html/tasks/implementation-planning.template.html +78 -19
  65. package/runtime/templates/reports/html/tasks/implementation.template.html +34 -16
  66. package/runtime/templates/reports/html/tasks/improvement-discovery.template.html +11 -11
  67. package/runtime/templates/reports/html/tasks/project-analysis.template.html +65 -25
  68. package/runtime/templates/reports/html/tasks/release-handoff.template.html +28 -14
  69. package/runtime/templates/reports/html/tasks/requirements-discovery.template.html +35 -15
  70. package/runtime/templates/reports/report.js +8 -5
  71. package/runtime/validators/validate-report-views.py +1 -1
  72. package/runtime/validators/validate-run.py +28 -31
  73. package/runtime/validators/validate_analysis_report.py +36 -0
  74. package/src/cli-registry.mjs +11 -0
  75. package/src/commands/inspect/worker-liveness.mjs +9 -7
  76. package/src/commands/report/translate.mjs +31 -0
  77. package/src/lib/helper-scripts.mjs +1 -0
  78. package/runtime/templates/reports/i18n/ko.json +0 -273
@@ -4,37 +4,44 @@
4
4
 
5
5
  {% block human_content %}
6
6
  <section data-report-section="symptom">
7
- <h2>사용자가 겪은 증상</h2>
8
- {{ render_narrative(narrative.symptomExplanation, "errorAnalysis.userNarrative.symptomExplanation", evidenceIndex) }}
7
+ <h2>{{ t('tasks.error-analysis.what-you-saw') }}</h2>
8
+ {{ render_narrative(narrative.symptomExplanation, "errorAnalysis.userNarrative.symptomExplanation") }}
9
9
  <blockquote data-report-field="errorAnalysis.symptomVerbatim">{{ error.symptomVerbatim | inline_code }}</blockquote>
10
10
  <p data-report-field="errorAnalysis.observableFailure">{{ error.observableFailure | inline_code }}</p>
11
11
  </section>
12
12
 
13
13
  <section data-report-section="reproduction">
14
- <h2>재현 상태</h2>
14
+ <h2>{{ t('tasks.error-analysis.reproduction') }}</h2>
15
15
  <p><span class="status status-{{ error.reproduction.status }}">{{ error.reproduction.status }}</span></p>
16
- <ul>{% for item in error.reproduction.evidence %}<li>{{ [item] | evidence_refs(evidenceIndex) }}</li>{% endfor %}</ul>
16
+ <ul>{% for item in error.reproduction.evidence %}<li>{{ [item] | evidence_refs }}</li>{% endfor %}</ul>
17
17
  {% if error.reproduction.blockedReason %}<p>{{ error.reproduction.blockedReason | inline_code }}</p>{% endif %}
18
18
  </section>
19
19
 
20
20
  <section data-report-section="causes">
21
- <h2>원인 후보</h2>
22
- {{ render_narrative(narrative.causeExplanation, "errorAnalysis.userNarrative.causeExplanation", evidenceIndex) }}
21
+ <h2>{{ t('tasks.error-analysis.cause-candidates') }}</h2>
22
+ {{ render_narrative(narrative.causeExplanation, "errorAnalysis.userNarrative.causeExplanation") }}
23
23
  {{ figure(causeFigure) }}
24
- <div class="summary-grid">{% for row in error.causeCandidates %}<article class="summary-card tone-{{ row.confidence }}"><p class="eyebrow">{{ row.id }} · 신뢰도 {{ row.confidence }}</p><h3>{{ row.statement | inline_code }}</h3><p class="evidence-refs">지지 근거: {{ row.supportingEvidence | evidence_refs(evidenceIndex) }}</p></article>{% else %}<p>현재 확인된 원인 후보가 없습니다.</p>{% endfor %}</div>
24
+ <div class="summary-grid">{% for row in error.causeCandidates %}<article class="summary-card tone-{{ row.confidence }}" id="id-{{ row.id }}"><p class="eyebrow">{{ row.id }} · Confidence {{ row.confidence }}</p><h3>{{ row.statement | inline_code }}</h3><p class="evidence-refs">Supporting evidence: {{ row.supportingEvidence | evidence_refs }}</p></article>{% else %}<p>{{ t('tasks.error-analysis.no-cause-candidate-has-been-established-yet') }}</p>{% endfor %}</div>
25
25
  </section>
26
26
 
27
27
  <section data-report-section="uncertainty">
28
- <h2>아직 확정할 수 없는 부분</h2>
29
- {{ render_narrative(narrative.uncertaintyExplanation, "errorAnalysis.userNarrative.uncertaintyExplanation", evidenceIndex) }}
30
- {% for row in error.causeCandidates %}<article class="summary-card"><h3>{{ row.id }}</h3><p data-report-field="errorAnalysis.causeCandidates.confidence"><strong>현재 신뢰도:</strong> {{ row.confidence }}</p><p data-report-field="errorAnalysis.causeCandidates.falsifyingEvidenceChecked"><strong>확인한 반증:</strong> {{ row.falsifyingEvidenceChecked | join(', ') | inline_code }}</p><p data-report-field="errorAnalysis.causeCandidates.disproveWith"><strong>이 가설을 기각하는 방법:</strong> {{ row.disproveWith | inline_code }}</p></article>{% endfor %}
28
+ <h2>{{ t('tasks.error-analysis.what-cannot-be-settled-yet') }}</h2>
29
+ {{ render_narrative(narrative.uncertaintyExplanation, "errorAnalysis.userNarrative.uncertaintyExplanation") }}
30
+ {% for row in error.causeCandidates %}<article class="summary-card" id="id-{{ row.id }}-detail"><h3>{{ row.id }}</h3><p data-report-field="errorAnalysis.causeCandidates.confidence"><strong>{{ t('tasks.error-analysis.confidence-now') }}</strong> {{ row.confidence }}</p><p data-report-field="errorAnalysis.causeCandidates.falsifyingEvidenceChecked"><strong>{{ t('tasks.error-analysis.disproof-attempted') }}</strong> {{ row.falsifyingEvidenceChecked | join(', ') | inline_code }}</p><p data-report-field="errorAnalysis.causeCandidates.disproveWith"><strong>{{ t('tasks.error-analysis.how-to-disprove-this') }}</strong> {{ row.disproveWith | inline_code }}</p></article>{% endfor %}
31
31
  </section>
32
32
 
33
33
  <section data-report-section="next-diagnostic">
34
- <h2>다음 진단 한 가지</h2>
35
- {{ render_narrative(narrative.diagnosticGuidance, "errorAnalysis.userNarrative.diagnosticGuidance", evidenceIndex) }}
36
- {{ summary_card("실행", error.nextDiagnostic.action, "important") }}
37
- <p><strong>확인 신호:</strong> {{ error.nextDiagnostic.confirmingSignal | inline_code }}</p>
38
- <p><strong>기각 신호:</strong> {{ error.nextDiagnostic.rejectingSignal | inline_code }}</p>
34
+ <h2>{{ t('tasks.error-analysis.the-one-diagnostic-to-run-next') }}</h2>
35
+ {{ render_narrative(narrative.diagnosticGuidance, "errorAnalysis.userNarrative.diagnosticGuidance") }}
36
+ {{ summary_card("Run", error.nextDiagnostic.action, "important") }}
37
+ <p><strong>{{ t('tasks.error-analysis.signal-to-check') }}</strong> {{ error.nextDiagnostic.confirmingSignal | inline_code }}</p>
38
+ <p><strong>{{ t('tasks.error-analysis.signal-that-rejects-it') }}</strong> {{ error.nextDiagnostic.rejectingSignal | inline_code }}</p>
39
+ </section>
40
+
41
+ <section data-report-section="routing" data-report-field="errorAnalysis.routing">
42
+ <h2>{{ t('tasks.error-analysis.next-step') }}</h2>
43
+ <p><strong>{{ t('tasks.error-analysis.recommended-task') }}</strong> — <code>{{ error.routing.nextTaskType }}</code></p>
44
+ <p><strong>{{ t('tasks.error-analysis.the-cause-this-rests-on') }}</strong> — {{ error.routing.leadingCauseId | inline_code }}</p>
45
+ <p>{{ error.routing.rationale | inline_code }}</p>
39
46
  </section>
40
47
  {% endblock %}
@@ -5,35 +5,55 @@
5
5
 
6
6
  {% block human_content %}
7
7
  <section data-report-section="feature-overview">
8
- <h2>기능 개요</h2>
9
- {{ render_narrative(narrative.userBehaviorExplanation, "featureAnalysis.userNarrative.userBehaviorExplanation", evidenceIndex) }}
10
- <p><strong>대상:</strong> {{ feature.target.requestedValue | inline_code }}</p>
8
+ <h2>{{ t('tasks.feature-analysis.feature-overview') }}</h2>
9
+ {{ render_narrative(narrative.userBehaviorExplanation, "featureAnalysis.userNarrative.userBehaviorExplanation") }}
10
+ <p><strong>{{ t('tasks.feature-analysis.subject') }}</strong> {{ feature.target.requestedValue | inline_code }}</p>
11
11
  <ul>{% for row in feature.actors %}<li>{{ row.description | inline_code }}</li>{% endfor %}</ul>
12
12
  </section>
13
13
 
14
14
  <section data-report-section="behavior-flow" data-report-field="featureAnalysis.flows">
15
- <h2>사용자 행동 흐름</h2>
16
- {{ render_narrative(narrative.flowExplanation, "featureAnalysis.userNarrative.flowExplanation", evidenceIndex) }}
15
+ <h2>{{ t('tasks.feature-analysis.how-a-user-moves-through-it') }}</h2>
16
+ {{ render_narrative(narrative.flowExplanation, "featureAnalysis.userNarrative.flowExplanation") }}
17
17
  {{ figure(flowFigure) }}
18
- <div class="summary-grid">{% for row in feature.flows %}<article class="summary-card" data-flow-kind="{{ 'alternate' if row.kind == 'alternative' else row.kind }}"><p class="eyebrow">{{ row.id }} · {{ row.kind }}</p><ol>{% for step in row.steps %}<li>{{ step.action | inline_code }}</li>{% endfor %}</ol></article>{% endfor %}</div>
18
+ <div class="summary-grid">{% for row in feature.flows %}<article class="summary-card" id="id-{{ row.id }}" data-flow-kind="{{ 'alternate' if row.kind == 'alternative' else row.kind }}"><p class="eyebrow">{{ row.id }} · {{ row.kind }}</p><ol>{% for step in row.steps %}<li>{{ step.action | inline_code }}</li>{% endfor %}</ol></article>{% endfor %}</div>
19
19
  </section>
20
20
 
21
21
  <section data-report-section="rules-and-state">
22
- <h2>규칙과 상태 변경</h2>
23
- {{ render_narrative(narrative.rulesAndStateExplanation, "featureAnalysis.userNarrative.rulesAndStateExplanation", evidenceIndex) }}
24
- <div data-report-field="featureAnalysis.domainRules"><h3>도메인 규칙</h3>{% for row in feature.domainRules %}{{ summary_card(row.id ~ " · " ~ (row.condition | inline_code), row.outcome) }}{% endfor %}</div>
25
- <div data-report-field="featureAnalysis.stateChanges"><h3>상태 변경</h3>{% for row in feature.stateChanges %}{{ summary_card(row.id ~ " · " ~ row.field, row.change) }}{% endfor %}</div>
22
+ <h2>{{ t('tasks.feature-analysis.rules-and-state-changes') }}</h2>
23
+ {{ render_narrative(narrative.rulesAndStateExplanation, "featureAnalysis.userNarrative.rulesAndStateExplanation") }}
24
+ <div data-report-field="featureAnalysis.domainRules"><h3>{{ t('tasks.feature-analysis.domain-rules') }}</h3>{% for row in feature.domainRules %}{{ summary_card(row.id ~ " · " ~ (row.condition | inline_code), row.outcome, anchor=row.id) }}{% endfor %}</div>
25
+ <div data-report-field="featureAnalysis.stateChanges"><h3>{{ t('tasks.feature-analysis.state-change') }}</h3>{% for row in feature.stateChanges %}{{ summary_card(row.id ~ " · " ~ row.field, row.change, anchor=row.id) }}{% endfor %}</div>
26
26
  </section>
27
27
 
28
28
  <section data-report-section="external-interactions" data-report-field="featureAnalysis.externalInteractions">
29
- <h2>외부 상호작용</h2>
30
- {% for row in feature.externalInteractions %}{{ summary_card(row.target | inline_code, row.input ~ " → " ~ row.output ~ ". 실패 시: " ~ row.failureHandling) }}{% endfor %}
29
+ <h2>{{ t('tasks.feature-analysis.external-interactions') }}</h2>
30
+ {% for row in feature.externalInteractions %}{{ summary_card(row.target | inline_code, row.input ~ " → " ~ row.output ~ ". On failure: " ~ row.failureHandling) }}{% endfor %}
31
31
  </section>
32
32
 
33
33
  <section data-report-section="test-gaps" data-report-field="featureAnalysis.testCoverage">
34
- <h2>테스트와 공백</h2>
35
- {{ render_narrative(narrative.testGapExplanation, "featureAnalysis.userNarrative.testGapExplanation", evidenceIndex) }}
36
- {% for row in feature.testCoverage %}{{ summary_card(row.id, "테스트: " ~ (row.testPaths | join(', ')) ~ "; 공백: " ~ ((row.gaps | join(', ')) or '없음')) }}{% endfor %}
34
+ <h2>{{ t('tasks.feature-analysis.tests-and-gaps') }}</h2>
35
+ {{ render_narrative(narrative.testGapExplanation, "featureAnalysis.userNarrative.testGapExplanation") }}
36
+ {% for row in feature.testCoverage %}{{ summary_card(row.id, "Tests: " ~ (row.testPaths | join(', ')) ~ "; Gaps: " ~ ((row.gaps | join(', ')) or 'none'), anchor=row.id) }}{% endfor %}
37
+ </section>
38
+
39
+ <section data-report-section="preconditions" data-report-field="featureAnalysis.preconditions">
40
+ <h2>{{ t('tasks.feature-analysis.precondition') }}</h2>
41
+ <table><thead><tr><th>{{ t('tasks.feature-analysis.body') }}</th><th>{{ t('tasks.feature-analysis.evidence') }}</th></tr></thead><tbody>{% for row in feature.preconditions %}<tr><td>{{ row.description | inline_code }}</td><td>{{ row.currentCodeEvidence | code_evidence }}</td></tr>{% else %}<tr><td colspan="2">{{ t('tasks.feature-analysis.nothing-was-recorded') }}</td></tr>{% endfor %}</tbody></table>
42
+ </section>
43
+
44
+ <section data-report-section="postconditions" data-report-field="featureAnalysis.postconditions">
45
+ <h2>{{ t('tasks.feature-analysis.state-once-it-is-done') }}</h2>
46
+ <table><thead><tr><th>{{ t('tasks.feature-analysis.body') }}</th><th>{{ t('tasks.feature-analysis.evidence') }}</th></tr></thead><tbody>{% for row in feature.postconditions %}<tr><td>{{ row.description | inline_code }}</td><td>{{ row.currentCodeEvidence | code_evidence }}</td></tr>{% else %}<tr><td colspan="2">{{ t('tasks.feature-analysis.nothing-was-recorded') }}</td></tr>{% endfor %}</tbody></table>
47
+ </section>
48
+
49
+ <section data-report-section="permissions-and-flags" data-report-field="featureAnalysis.permissionsAndFlags">
50
+ <h2>{{ t('tasks.feature-analysis.permissions-and-flags') }}</h2>
51
+ <table><thead><tr><th>{{ t('tasks.feature-analysis.body') }}</th><th>{{ t('tasks.feature-analysis.evidence') }}</th></tr></thead><tbody>{% for row in feature.permissionsAndFlags %}<tr><td>{{ row.description | inline_code }}</td><td>{{ row.currentCodeEvidence | code_evidence }}</td></tr>{% else %}<tr><td colspan="2">{{ t('tasks.feature-analysis.nothing-was-recorded') }}</td></tr>{% endfor %}</tbody></table>
52
+ </section>
53
+
54
+ <section data-report-section="non-functional" data-report-field="featureAnalysis.nonFunctionalConcerns">
55
+ <h2>{{ t('tasks.feature-analysis.non-functional-concerns') }}</h2>
56
+ <table><thead><tr><th>{{ t('tasks.feature-analysis.body') }}</th><th>{{ t('tasks.feature-analysis.evidence') }}</th></tr></thead><tbody>{% for row in feature.nonFunctionalConcerns %}<tr><td>{{ row.description | inline_code }}</td><td>{{ row.currentCodeEvidence | code_evidence }}</td></tr>{% else %}<tr><td colspan="2">{{ t('tasks.feature-analysis.nothing-was-recorded') }}</td></tr>{% endfor %}</tbody></table>
37
57
  </section>
38
58
 
39
59
  {{ analysis_review(analysisReviewIds) }}
@@ -4,36 +4,43 @@
4
4
 
5
5
  {% block human_content %}
6
6
  <section data-report-section="verification-verdict">
7
- <h2>검증 판정</h2>
7
+ <h2>{{ t('tasks.final-verification.verification-verdict') }}</h2>
8
8
  <p><span class="status status-{{ verdict.verdictToken }}">{{ verdict.verdictToken }}</span></p>
9
- {{ render_narrative(narrative.verdictExplanation, "finalVerification.userNarrative.verdictExplanation", evidenceIndex) }}
9
+ {{ render_narrative(narrative.verdictExplanation, "finalVerification.userNarrative.verdictExplanation") }}
10
10
  </section>
11
11
 
12
12
  <section data-report-section="requirement-results" data-report-field="finalVerification.validationEvidence">
13
- <h2>요구사항별 결과</h2>
14
- {{ render_narrative(narrative.coverageExplanation, "finalVerification.userNarrative.coverageExplanation", evidenceIndex) }}
15
- {{ figure(coverageFigure) }}
13
+ <h2>{{ t('tasks.final-verification.result-per-requirement') }}</h2>
14
+ {{ render_narrative(narrative.coverageExplanation, "finalVerification.userNarrative.coverageExplanation") }}
15
+ {{ figure(coverageFigure, anchor_nodes=true) }}
16
16
  </section>
17
17
 
18
18
  <section data-report-section="blockers" data-report-field="finalVerification.acceptanceBlockers">
19
- <h2>수락 차단 항목</h2>
20
- {{ render_narrative(narrative.blockerExplanation, "finalVerification.userNarrative.blockerExplanation", evidenceIndex) }}
21
- <div class="summary-grid">{% for row in final.acceptanceBlockers %}{{ summary_card(row.id ~ " · " ~ row.severity, row.statement, "important") }}{% else %}<p>수락을 막는 항목이 없습니다.</p>{% endfor %}</div>
19
+ <h2>{{ t('tasks.final-verification.what-blocks-acceptance') }}</h2>
20
+ {{ render_narrative(narrative.blockerExplanation, "finalVerification.userNarrative.blockerExplanation") }}
21
+ <div class="summary-grid">{% for row in final.acceptanceBlockers %}{{ summary_card(row.id ~ " · " ~ row.severity, row.statement, "important", anchor=row.id) }}{% else %}<p>{{ t('tasks.final-verification.nothing-is-holding-acceptance') }}</p>{% endfor %}</div>
22
22
  </section>
23
23
 
24
24
  <section data-report-section="residual-risk" data-report-field="finalVerification.residualRisk">
25
- <h2>잔여 위험</h2>
26
- <div class="summary-grid">{% for row in final.residualRisk %}<article class="summary-card"><h3>{{ row.id }}</h3><p>{{ row.item | inline_code }}</p><p><strong>담당:</strong> {{ row.owner | inline_code }}</p><p><strong>상향 조건:</strong> {{ row.escalationTrigger | inline_code }}</p></article>{% else %}<p>기록된 잔여 위험이 없습니다.</p>{% endfor %}</div>
25
+ <h2>{{ t('tasks.final-verification.residual-risk') }}</h2>
26
+ <div class="summary-grid">{% for row in final.residualRisk %}<article class="summary-card" id="id-{{ row.id }}"><h3>{{ row.id }}</h3><p>{{ row.item | inline_code }}</p><p><strong>{{ t('tasks.final-verification.owner') }}</strong> {{ row.owner | inline_code }}</p><p><strong>{{ t('tasks.final-verification.escalation-condition') }}</strong> {{ row.escalationTrigger | inline_code }}</p></article>{% else %}<p>{{ t('tasks.final-verification.no-residual-risk-was-recorded') }}</p>{% endfor %}</div>
27
27
  </section>
28
28
 
29
29
  <section data-report-section="manual-results" data-report-field="finalVerification.manualUserTest">
30
- <h2>수동 사용자 테스트 결과</h2>
30
+ <h2>{{ t('tasks.final-verification.manual-user-test-results') }}</h2>
31
31
  {% if final.manualUserTest.applicable %}{% for row in final.manualUserTest.results %}<article class="summary-card tone-{{ row.result }}"><h3>{{ row.target | inline_code }} · {{ row.result }}</h3><p>{{ row.performed | inline_code }}</p><p>{{ row.observed | inline_code }}</p></article>{% endfor %}{% else %}<p>{{ final.manualUserTest.exemptionReaffirm | inline_code }}</p>{% endif %}
32
32
  </section>
33
33
 
34
+ <section data-report-section="verified-source" data-report-field="finalVerification.sourceImplementationReport">
35
+ <h2>{{ t('tasks.final-verification.what-was-verified') }}</h2>
36
+ <table><tbody><tr><th>{{ t('tasks.final-verification.implementation-report') }}</th><td><code>{{ final.sourceImplementationReport.path }}</code></td></tr><tr><th>{{ t('tasks.final-verification.worktree') }}</th><td><code>{{ final.sourceImplementationReport.worktreePath }}</code></td></tr><tr><th>{{ t('tasks.final-verification.base-commit') }}</th><td><code>{{ final.sourceImplementationReport.implementationBaseRef }}</code> → <code>{{ final.sourceImplementationReport.capturedHeadSha }}</code></td></tr><tr><th>{{ t('tasks.final-verification.diff-summary') }}</th><td>{{ final.sourceImplementationReport.diffSummaryQuote | inline_code }}</td></tr><tr><th>{{ t('tasks.final-verification.uncommitted-state') }}</th><td>{{ final.sourceImplementationReport.gitStatusShort | inline_code }}</td></tr></tbody></table>
37
+ {% if final.get("stageReports") %}<h3>{{ t('tasks.final-verification.per-stage-reports') }}</h3>
38
+ <ul data-report-field="finalVerification.stageReports">{% for row in final.stageReports %}<li>Stage {{ row.stage }} — <code>{{ row.reportPath }}</code></li>{% endfor %}</ul>{% endif %}
39
+ </section>
40
+
34
41
  <section data-report-section="release-route">
35
- <h2>릴리스 경로</h2>
36
- {{ render_narrative(narrative.releaseReadiness, "finalVerification.userNarrative.releaseReadiness", evidenceIndex) }}
37
- {% if releaseAllowed %}<p>{{ final.routingRecommendation | inline_code }}</p>{% else %}<p>현재 판정으로는 release-handoff를 시작할 수 없습니다. 차단 또는 조건을 먼저 해소하세요.</p>{% endif %}
42
+ <h2>{{ t('tasks.final-verification.release-path') }}</h2>
43
+ {{ render_narrative(narrative.releaseReadiness, "finalVerification.userNarrative.releaseReadiness") }}
44
+ {% if releaseAllowed %}<p>{{ final.routingRecommendation | inline_code }}</p>{% else %}<p>{{ t('tasks.final-verification.the-current-verdict-does-not-allow-release-h') }}</p>{% endif %}
38
45
  </section>
39
46
  {% endblock %}
@@ -1,45 +1,104 @@
1
1
  {% extends "html/base.template.html" %}
2
2
  {% from "html/macros/forms.html" import plan_approval %}
3
- {% from "html/macros/layout.html" import narrative as render_narrative, action_list, summary_card %}
3
+ {% from "html/macros/layout.html" import narrative as render_narrative, action_list, summary_card, row_key %}
4
4
  {% from "html/macros/visualizations.html" import figure %}
5
5
 
6
6
  {% block human_content %}
7
7
  <section data-report-section="planning-goal">
8
- <h2>계획 목표</h2>
8
+ <h2>{{ t('tasks.implementation-planning.what-the-plan-is-for') }}</h2>
9
9
  <p>{{ humanSummary.outcome | inline_code }}</p>
10
- <div class="summary-grid">{% for reason in humanSummary.whyItMatters %}{{ summary_card("왜 중요한가 " ~ loop.index, reason, "important") }}{% endfor %}</div>
10
+ <div class="summary-grid">{% for reason in humanSummary.whyItMatters %}{{ summary_card("", reason, "important") }}{% endfor %}</div>
11
11
  </section>
12
12
 
13
13
  <section data-report-section="option-comparison">
14
- <h2>구현 대안 비교</h2>
15
- {{ render_narrative(narrative.optionExplanation, "implementationPlanning.userNarrative.optionExplanation", evidenceIndex) }}
16
- <div class="summary-grid" data-report-field="implementationPlanning.optionCandidates">{% for row in planning.optionCandidates %}<article class="summary-card"><h3>{{ row.name | inline_code }}</h3><p><strong>인터페이스:</strong> {{ row.interfaces | inline_code }}</p><p><strong>영향 범위:</strong> {{ row.blastRadius | inline_code }}</p><ul>{% for file in row.fileStructure %}<li>{{ file.action }} <code>{{ file.path }}</code> — {{ file.summary | inline_code }}</li>{% endfor %}</ul></article>{% endfor %}</div>
17
- <table data-report-field="implementationPlanning.tradeoffMatrix"><thead><tr><th>옵션</th><th>복잡도</th><th>위험</th><th>가역성</th><th>테스트 비용</th><th>배포 비용</th></tr></thead><tbody>{% for row in planning.tradeoffMatrix %}<tr><td>{{ row.option | inline_code }}</td><td>{{ row.complexity | inline_code }}</td><td>{{ row.risk | inline_code }}</td><td>{{ row.reversibility | inline_code }}</td><td>{{ row.testCoverageCost | inline_code }}</td><td>{{ row.rolloutCost | inline_code }}</td></tr>{% endfor %}</tbody></table>
14
+ <h2>{{ t('tasks.implementation-planning.implementation-options-compared') }}</h2>
15
+ {{ render_narrative(narrative.optionExplanation, "implementationPlanning.userNarrative.optionExplanation") }}
16
+ <div class="summary-grid" data-report-field="implementationPlanning.optionCandidates">{% for row in planning.optionCandidates %}<article class="summary-card"><h3>{{ row.name | inline_code }}</h3><p><strong>{{ t('tasks.implementation-planning.interface') }}</strong> {{ row.interfaces | inline_code }}</p><p><strong>{{ t('tasks.implementation-planning.blast-radius') }}</strong> {{ row.blastRadius | inline_code }}</p><ul>{% for file in row.fileStructure %}<li id="id-{{ file.id }}">{{ file.action }} <code>{{ file.path }}</code> — {{ file.summary | inline_code }}</li>{% endfor %}</ul></article>{% endfor %}</div>
17
+ <table data-report-field="implementationPlanning.tradeoffMatrix"><thead><tr><th>{{ t('tasks.implementation-planning.option') }}</th><th>{{ t('tasks.implementation-planning.complexity') }}</th><th>{{ t('tasks.implementation-planning.risk') }}</th><th>{{ t('tasks.implementation-planning.reversibility') }}</th><th>{{ t('tasks.implementation-planning.test-cost') }}</th><th>{{ t('tasks.implementation-planning.rollout-cost') }}</th></tr></thead><tbody>{% for row in planning.tradeoffMatrix %}<tr><td>{{ row.option | inline_code }}</td><td>{{ row.complexity | inline_code }}</td><td>{{ row.risk | inline_code }}</td><td>{{ row.reversibility | inline_code }}</td><td>{{ row.testCoverageCost | inline_code }}</td><td>{{ row.rolloutCost | inline_code }}</td></tr>{% endfor %}</tbody></table>
18
18
  </section>
19
19
 
20
20
  <section data-report-section="recommendation" data-report-field="implementationPlanning.recommendedOption">
21
- <h2>권장안</h2>
22
- {{ render_narrative(narrative.recommendationExplanation, "implementationPlanning.userNarrative.recommendationExplanation", evidenceIndex) }}
23
- <article class="summary-card tone-important"><h3>{{ planning.recommendedOption.name | inline_code }}</h3><p>{{ planning.recommendedOption.coreReason | inline_code }}</p><p>{{ planning.recommendedOption.rationale | inline_code }}</p><p><strong>선택하지 않은 안:</strong> {{ planning.recommendedOption.rejectedSummary | inline_code }}</p></article>
21
+ <h2>{{ t('tasks.implementation-planning.recommended') }}</h2>
22
+ {{ render_narrative(narrative.recommendationExplanation, "implementationPlanning.userNarrative.recommendationExplanation") }}
23
+ <article class="summary-card tone-important"><h3>{{ planning.recommendedOption.name | inline_code }}</h3><p>{{ planning.recommendedOption.coreReason | inline_code }}</p><p>{{ planning.recommendedOption.rationale | inline_code }}</p><p><strong>{{ t('tasks.implementation-planning.options-not-taken') }}</strong> {{ planning.recommendedOption.rejectedSummary | inline_code }}</p></article>
24
24
  </section>
25
25
 
26
26
  <section data-report-section="stage-map" data-report-field="implementationPlanning.stageMap">
27
- <h2>단계별 실행 지도</h2>
28
- {{ render_narrative(narrative.stageStrategy, "implementationPlanning.userNarrative.stageStrategy", evidenceIndex) }}
27
+ <h2>{{ t('tasks.implementation-planning.stage-map') }}</h2>
28
+ {{ render_narrative(narrative.stageStrategy, "implementationPlanning.userNarrative.stageStrategy") }}
29
29
  {{ figure(stageFigure) }}
30
- <div class="summary-grid">{% for row in planning.stages %}<article class="summary-card"><h3>Stage {{ row.stage }} · {{ row.title | inline_code }}</h3><p>{{ row.sliceValue | inline_code }}</p><p><strong>완료 조건:</strong> {{ row.acceptance | inline_code }}</p><p><strong>검증:</strong> {{ row.stageValidation | inline_code }}</p></article>{% endfor %}</div>
30
+ <div class="summary-grid">{% for row in planning.stages %}<article class="summary-card"><h3>Stage {{ row.stage }} · {{ row.title | inline_code }}</h3><p>{{ row.sliceValue | inline_code }}</p><p><strong>{{ t('tasks.implementation-planning.exit-contract') }}</strong> {{ row.acceptance | inline_code }}</p><p><strong>{{ t('tasks.implementation-planning.verification') }}</strong> {{ row.stageValidation | inline_code }}</p></article>{% endfor %}</div>
31
31
  </section>
32
32
 
33
+ {% if planning.get("designPreparation") %}
34
+ <section data-report-section="design-preparation" data-report-field="implementationPlanning.designPreparation">
35
+ <h2>{{ t('tasks.implementation-planning.design-preparation') }}</h2>
36
+ {% if planning.designPreparation.mode == "no-design-inputs" %}
37
+ <p>{{ planning.designPreparation.reason | inline_code }}</p>
38
+ {% else %}
39
+ <table><thead><tr><th>{{ t('tasks.implementation-planning.design-item') }}</th><th>{{ t('tasks.implementation-planning.what-it-needs') }}</th><th>{{ t('tasks.implementation-planning.open-questions') }}</th></tr></thead><tbody>{% for row in planning.designPreparation["items"] %}<tr id="id-{{ row.id }}">{{ row_key(pairs=[("ID", row.id), ("Subject", row.title), ("Kind", row.kind)], status_name="Status", status_raw=row.status, status_text=row.status) }}<td>{{ row.need | inline_code }}</td><td>{{ row.openQuestions | default([]) | join(" · ") | inline_code }}</td></tr>{% endfor %}</tbody></table>
40
+ {% endif %}
41
+ </section>
42
+ {% endif %}
43
+
44
+ <section data-report-section="variation-points" data-report-field="implementationPlanning.variationPointAnalysis">
45
+ <h2>{{ t('tasks.implementation-planning.where-implementations-diverge') }}</h2>
46
+ {% if planning.variationPointAnalysis.hasMultipleImplementations %}
47
+ <table><thead><tr><th>{{ t('tasks.implementation-planning.action') }}</th><th>{{ t('tasks.implementation-planning.implementations') }}</th><th>{{ t('tasks.implementation-planning.extraction-call') }}</th><th>{{ t('tasks.implementation-planning.evidence') }}</th></tr></thead><tbody>{% for row in planning.variationPointAnalysis.points %}<tr><td>{{ row.behavior | inline_code }}</td><td>{{ row.implementations | default([]) | join(" · ") | inline_code }}</td><td>{{ row.extractionDecision | inline_code }}</td><td>{{ row.evidence | inline_code }}</td></tr>{% endfor %}</tbody></table>
48
+ {% else %}<p>{{ planning.variationPointAnalysis.noVariationRationale | inline_code }}</p>{% endif %}
49
+ </section>
50
+
51
+ {% if planning.get("stepwiseExecution") %}
52
+ <section data-report-section="stepwise-execution" data-report-field="implementationPlanning.stepwiseExecution">
53
+ <h2>{{ t('tasks.implementation-planning.step-by-step-execution') }}</h2>
54
+ <table><thead><tr><th>#</th><th>{{ t('tasks.implementation-planning.action') }}</th><th>{{ t('tasks.implementation-planning.files') }}</th><th>{{ t('tasks.implementation-planning.command-test') }}</th><th>{{ t('tasks.implementation-planning.expected') }}</th></tr></thead><tbody>{% for row in planning.stepwiseExecution %}<tr><td>{{ row.step }}</td><td>{{ row.action | inline_code }}</td><td>{% for file in row.files %}<code>{{ file }}</code>{% if not loop.last %} {% endif %}{% endfor %}</td><td><code>{{ row.commandOrTest }}</code></td><td>{{ row.expectedOutcome | inline_code }}</td></tr>{% endfor %}</tbody></table>
55
+ </section>
56
+ {% endif %}
57
+
33
58
  <section data-report-section="validation-and-rollback">
34
- <h2>검증과 롤백</h2>
35
- {{ render_narrative(narrative.validationAndRollback, "implementationPlanning.userNarrative.validationAndRollback", evidenceIndex) }}
36
- <h3>검증 체크리스트</h3><table data-report-field="implementationPlanning.validationChecklist"><thead><tr><th>ID</th><th>시점</th><th>확인</th><th>명령</th><th>기대 결과</th></tr></thead><tbody>{% for row in planning.validationChecklist %}<tr><td>{{ row.id }}</td><td>{{ row.phase }}</td><td>{{ row.check | inline_code }}</td><td><code>{{ row.commandOrObservation }}</code></td><td>{{ row.expectedOutcome | inline_code }}</td></tr>{% endfor %}</tbody></table>
37
- <h3>롤백 전략</h3><div class="summary-grid" data-report-field="implementationPlanning.rollbackStrategy">{% for row in planning.rollbackStrategy %}{{ summary_card(row.id ~ " · " ~ (row.triggerSignal | inline_code), row.action, "important") }}{% endfor %}</div>
59
+ <h2>{{ t('tasks.implementation-planning.verification-and-rollback') }}</h2>
60
+ {{ render_narrative(narrative.validationAndRollback, "implementationPlanning.userNarrative.validationAndRollback") }}
61
+ <h3>{{ t('tasks.implementation-planning.validation-checklist') }}</h3><table data-report-field="implementationPlanning.validationChecklist"><thead><tr><th>{{ t('tasks.implementation-planning.check') }}</th><th>{{ t('tasks.implementation-planning.check') }}</th><th>{{ t('tasks.implementation-planning.command') }}</th><th>{{ t('tasks.implementation-planning.expected') }}</th></tr></thead><tbody>{% for row in planning.validationChecklist %}<tr id="id-{{ row.id }}">{{ row_key(pairs=[("ID", row.id), ("Phase", row.phase)]) }}<td>{{ row.check | inline_code }}</td><td><code>{{ row.commandOrObservation }}</code></td><td>{{ row.expectedOutcome | inline_code }}</td></tr>{% endfor %}</tbody></table>
62
+ <h3>{{ t('tasks.implementation-planning.how-each-requirement-gets-met') }}</h3><table data-report-field="implementationPlanning.requirementCoverage"><thead><tr><th>{{ t('tasks.implementation-planning.requirement') }}</th><th>{{ t('tasks.implementation-planning.body') }}</th><th>{{ t('tasks.implementation-planning.covered-by') }}</th></tr></thead><tbody>{% for row in planning.requirementCoverage %}<tr id="id-{{ row.id }}">{{ row_key(pairs=[("ID", row.id), ("Source", row.source)], status_name="Status", status_raw=row.status, status_text=row.status) }}<td>{{ row.requirement | inline_code }}</td><td>{{ row.coveredBy | inline_code }}</td></tr>{% endfor %}</tbody></table>
63
+ <h3>{{ t('tasks.implementation-planning.cross-project-dependencies') }}</h3><table data-report-field="implementationPlanning.crossProjectDependencies"><thead><tr><th>{{ t('tasks.implementation-planning.dependency') }}</th><th>{{ t('tasks.implementation-planning.work-needed') }}</th><th>{{ t('tasks.implementation-planning.signal-to-check') }}</th><th>{{ t('tasks.implementation-planning.how-to-start') }}</th></tr></thead><tbody>{% for row in planning.crossProjectDependencies %}<tr id="id-{{ row.id }}">{{ row_key(pairs=[("ID", row.id), ("Project", row.project), ("Direction", row.direction)]) }}<td>{{ row.requiredWork | inline_code }}</td><td>{{ row.verificationSignal | inline_code }}</td><td>{{ row.howToStart | inline_code }}</td></tr>{% endfor %}</tbody></table>
64
+ <h3>{{ t('tasks.implementation-planning.dependency-and-migration-risk') }}</h3><table data-report-field="implementationPlanning.dependencyMigrationRisk"><thead><tr><th>{{ t('tasks.implementation-planning.item') }}</th><th>{{ t('tasks.implementation-planning.impact') }}</th><th>{{ t('tasks.implementation-planning.mitigation') }}</th></tr></thead><tbody>{% for row in planning.dependencyMigrationRisk %}<tr id="id-{{ row.id }}">{{ row_key(pairs=[("ID", row.id), ("Item", row.item), ("Kind", row.kind)]) }}<td>{{ row.impact | inline_code }}</td><td>{{ row.mitigation | inline_code }}</td></tr>{% endfor %}</tbody></table>
65
+ <h3>{{ t('tasks.implementation-planning.rollback-strategy') }}</h3><div class="summary-grid" data-report-field="implementationPlanning.rollbackStrategy">{% for row in planning.rollbackStrategy %}{{ summary_card(row.id ~ " · " ~ (row.triggerSignal | inline_code), row.action, "important", anchor=row.id) }}{% endfor %}</div>
66
+ </section>
67
+
68
+ <section data-report-section="plan-verification" data-report-field="implementationPlanning.planBodyVerification">
69
+ <h2>{{ t('tasks.implementation-planning.plan-body-verification') }}</h2>
70
+ <p><strong>{{ t('tasks.implementation-planning.verdict') }}</strong> — {{ planning.planBodyVerification.gateResult | inline_code }} ({{ planning.planBodyVerification.roundCount }} rounds{% if planning.planBodyVerification.get("selfFixRoundsApplied") is not none %}, {{ planning.planBodyVerification.selfFixRoundsApplied }} self-fix rounds{% if planning.planBodyVerification.get("selfFixStopReason") %} · {{ planning.planBodyVerification.selfFixStopReason | inline_code }}{% endif %}{% endif %})</p>
71
+ {% if planning.planBodyVerification.get("gateBlockedBy") %}<p><strong>{{ t('tasks.implementation-planning.what-it-blocked') }}</strong> — {{ planning.planBodyVerification.gateBlockedBy | join(", ") | inline_code }}</p>{% endif %}
72
+ <table><thead><tr><th>{{ t('tasks.implementation-planning.plan-item') }}</th><th>{{ t('tasks.implementation-planning.subject') }}</th></tr></thead><tbody>{% for row in planning.planBodyVerification.planItems %}<tr id="id-{{ row.id }}">{{ row_key(pairs=[("ID", row.id), ("Source section", row.sourceSection)]) }}<td>{{ row.subject | inline_code }}</td></tr>{% endfor %}</tbody></table>
73
+ {% if planning.planBodyVerification.get("dissentLog") %}<h3>{{ t('tasks.implementation-planning.dissent-left-standing') }}</h3>
74
+ <table><thead><tr><th>{{ t('tasks.implementation-planning.subject') }}</th><th>{{ t('tasks.implementation-planning.body') }}</th></tr></thead><tbody>{% for row in planning.planBodyVerification.dissentLog %}<tr>{{ row_key(pairs=[("Subject", row.planItem), ("Worker", row.workerRole)]) }}<td>{{ row.body | inline_code }}</td></tr>{% endfor %}</tbody></table>{% endif %}
75
+ </section>
76
+
77
+ {% if planning.get("supersessionLedger") is not none %}
78
+ <section data-report-section="superseded" data-report-field="implementationPlanning.supersessionLedger">
79
+ <h2>{{ t('tasks.implementation-planning.what-your-answers-overturned') }}</h2>
80
+ <table><thead><tr><th>{{ t('tasks.implementation-planning.answer') }}</th><th>{{ t('tasks.implementation-planning.statement-overturned') }}</th><th>{{ t('tasks.implementation-planning.replaced-with') }}</th></tr></thead><tbody>{% for row in planning.supersessionLedger %}<tr>{{ row_key(pairs=[("Answer", row.clarificationId), ("Disposition", row.disposition)]) }}<td>{{ row.supersededStatement | inline_code }}</td><td>{{ row.replacedWith | inline_code }}</td></tr>{% else %}<tr><td colspan="4">{{ t('tasks.implementation-planning.no-answer-overturned-anything') }}</td></tr>{% endfor %}</tbody></table>
81
+ </section>
82
+ {% endif %}
83
+
84
+ {% if planning.get("incrementalDecision") %}<section data-report-section="replan-scope" data-report-field="implementationPlanning.incrementalDecision">
85
+ <h2>{{ t('tasks.implementation-planning.scope-restated') }}</h2>
86
+ <p><strong>{{ planning.incrementalDecision.mode | inline_code }}</strong> — {{ planning.incrementalDecision.reason | inline_code }}</p>
87
+ <p><strong>{{ t('tasks.implementation-planning.stages-verified-again') }}</strong> — {{ planning.incrementalDecision.reverifyStages | default([]) | join(", ") | inline_code }} · <strong>{{ t('tasks.implementation-planning.stages-carried-over-as-they-were') }}</strong> — {{ planning.incrementalDecision.carryStages | default([]) | join(", ") | inline_code }}</p>
88
+ </section>{% endif %}
89
+
90
+ <section data-report-section="decision-drafts" data-report-field="implementationPlanning.decisionDrafts">
91
+ <h2>{{ t('tasks.implementation-planning.decision-record-draft') }}</h2>
92
+ {% for row in planning.decisionDrafts %}
93
+ <article class="summary-card"><h3>{{ row.number }} · {{ row.slug | inline_code }} ({{ row.status | inline_code }})</h3><p><strong>{{ t('tasks.implementation-planning.context') }}</strong> — {{ row.context | inline_code }}</p><p><strong>{{ t('tasks.implementation-planning.decision') }}</strong> — {{ row.decision | inline_code }}</p><p><strong>{{ t('tasks.implementation-planning.result') }}</strong> — {{ row.consequences | inline_code }}</p></article>
94
+ {% else %}<p>{{ t('tasks.implementation-planning.there-is-no-decision-draft-to-record') }}</p>{% endfor %}
95
+ {% if planning.get("skippedAdrCandidates") %}<h3>{{ t('tasks.implementation-planning.what-was-deliberately-not-recorded') }}</h3>
96
+ <table data-report-field="implementationPlanning.skippedAdrCandidates"><thead><tr><th>{{ t('tasks.implementation-planning.topic') }}</th><th>{{ t('tasks.implementation-planning.why') }}</th></tr></thead><tbody>{% for row in planning.skippedAdrCandidates %}<tr><td>{{ row.topic | inline_code }}</td><td>{{ row.reason | inline_code }}</td></tr>{% endfor %}</tbody></table>{% endif %}
38
97
  </section>
39
98
 
40
99
  <section data-report-section="open-decisions">
41
- <h2>승인 전 열린 결정</h2>
42
- <div class="summary-grid">{% for row in openDecisions %}{{ summary_card(row.id ~ " · " ~ row.kind, row.statement, "important") }}{% else %}<p>승인을 막는 열린 결정이 없습니다.</p>{% endfor %}</div>
100
+ <h2>{{ t('tasks.implementation-planning.open-decisions-before-approval') }}</h2>
101
+ <div class="summary-grid">{% for row in openDecisions %}{{ summary_card(row.id ~ " · " ~ row.kind, row.statement, "important", anchor=row.id) }}{% else %}<p>{{ t('tasks.implementation-planning.no-open-decision-is-holding-approval') }}</p>{% endfor %}</div>
43
102
  {{ action_list(humanSummary.actions) }}
44
103
  </section>
45
104
 
@@ -1,40 +1,58 @@
1
1
  {% extends "html/base.template.html" %}
2
- {% from "html/macros/layout.html" import narrative as render_narrative, summary_card %}
2
+ {% from "html/macros/layout.html" import narrative as render_narrative, summary_card, row_key %}
3
3
  {% from "html/macros/visualizations.html" import figure %}
4
4
 
5
5
  {% block human_content %}
6
6
  <section data-report-section="delivered-outcome">
7
- <h2>전달된 결과</h2>
8
- {{ render_narrative(narrative.deliveredOutcome, "implementation.userNarrative.deliveredOutcome", evidenceIndex) }}
7
+ <h2>{{ t('tasks.implementation.what-was-delivered') }}</h2>
8
+ {{ render_narrative(narrative.deliveredOutcome, "implementation.userNarrative.deliveredOutcome") }}
9
9
  <p>{{ humanSummary.outcome | inline_code }}</p>
10
10
  </section>
11
11
 
12
12
  <section data-report-section="change-map" data-report-field="implementation.diffSummary">
13
- <h2>무엇이 달라졌는가</h2>
14
- {{ render_narrative(narrative.changeExplanation, "implementation.userNarrative.changeExplanation", evidenceIndex) }}
13
+ <h2>{{ t('tasks.implementation.what-changed') }}</h2>
14
+ {{ render_narrative(narrative.changeExplanation, "implementation.userNarrative.changeExplanation") }}
15
15
  {{ figure(changeFigure) }}
16
16
  </section>
17
17
 
18
18
  <section data-report-section="requirement-coverage">
19
- <h2>요구사항 충족</h2>
20
- <table data-report-field="implementation.requirementCoverage"><thead><tr><th>ID</th><th>요구사항</th><th>구현·증거</th><th>상태</th></tr></thead><tbody>{% for row in implementation.requirementCoverage %}<tr><td>{{ row.id }}</td><td>{{ row.requirement | inline_code }}</td><td>{{ row.coveredBy | inline_code }}</td><td><span class="status status-{{ row.status }}">{{ row.status }}</span></td></tr>{% endfor %}</tbody></table>
19
+ <h2>{{ t('tasks.implementation.requirement-coverage') }}</h2>
20
+ <table data-report-field="implementation.requirementCoverage"><thead><tr><th>{{ t('tasks.implementation.requirement') }}</th><th>{{ t('tasks.implementation.body') }}</th><th>{{ t('tasks.implementation.implemented-by') }}</th></tr></thead><tbody>{% for row in implementation.requirementCoverage %}<tr id="id-{{ row.id }}">{{ row_key(pairs=[("ID", row.id)], status_name="Status", status_raw=row.status, status_text=row.status) }}<td>{{ row.requirement | inline_code }}</td><td>{{ row.coveredBy | inline_code }}</td></tr>{% endfor %}</tbody></table>
21
21
  </section>
22
22
 
23
23
  <section data-report-section="validation" data-report-field="implementation.validationEvidence">
24
- <h2>검증 결과</h2>
25
- {{ render_narrative(narrative.validationExplanation, "implementation.userNarrative.validationExplanation", evidenceIndex) }}
26
- <div class="summary-grid">{% for row in implementation.validationEvidence %}{{ summary_card(row.phase ~ " · 종료 코드 " ~ row.exitCode, row.outputTail, "important" if row.exitCode else "neutral") }}{% endfor %}</div>
27
- <p><strong>독립 검증:</strong> {% for row in implementation.verifierResults %}<span class="status status-{{ row.verdict | lower }}">{{ row.verdict }}</span>{% if not loop.last %}, {% endif %}{% endfor %}</p>
24
+ <h2>{{ t('tasks.implementation.verification-result') }}</h2>
25
+ {{ render_narrative(narrative.validationExplanation, "implementation.userNarrative.validationExplanation") }}
26
+ <div class="summary-grid">{% for row in implementation.validationEvidence %}{{ summary_card(row.phase ~ " · exit code " ~ row.exitCode, row.outputTail, "important" if row.exitCode else "neutral") }}{% endfor %}</div>
27
+ <p><strong>{{ t('tasks.implementation.independent-check') }}</strong> {% for row in implementation.verifierResults %}<span class="status status-{{ row.verdict | lower }}">{{ row.verdict }}</span>{% if not loop.last %}, {% endif %}{% endfor %}</p>
28
28
  </section>
29
29
 
30
30
  <section data-report-section="remaining-issues">
31
- <h2>남은 문제</h2>
32
- {{ render_narrative(narrative.remainingIssues, "implementation.userNarrative.remainingIssues", evidenceIndex) }}
33
- <div class="summary-grid">{% for row in implementation.outOfPlanEdits %}{{ summary_card(row.id ~ " · " ~ row.file, row.rationale, "important") }}{% else %}<p>계획 밖 수정 사항이 없습니다.</p>{% endfor %}</div>
31
+ <h2>{{ t('tasks.implementation.what-is-left') }}</h2>
32
+ {{ render_narrative(narrative.remainingIssues, "implementation.userNarrative.remainingIssues") }}
33
+ <div class="summary-grid">{% for row in implementation.outOfPlanEdits %}{{ summary_card(row.id ~ " · " ~ row.file, row.rationale, "important", anchor=row.id) }}{% else %}<p>{{ t('tasks.implementation.nothing-was-changed-outside-the-plan') }}</p>{% endfor %}</div>
34
+ </section>
35
+
36
+ <section data-report-section="shipped-commits" data-report-field="implementation.commitList">
37
+ <h2>{{ t('tasks.implementation.commits-delivered') }}</h2>
38
+ {% if implementation.commitList is mapping %}<p>{{ t('tasks.implementation.there-are-no-commits') }}</p>{% else %}
39
+ <table><thead><tr><th>{{ t('tasks.implementation.commit') }}</th><th>{{ t('tasks.implementation.subject') }}</th><th>{{ t('tasks.implementation.files') }}</th></tr></thead><tbody>{% for row in implementation.commitList %}<tr>{{ row_key(pairs=[("SHA", row.shortSha), ("Order", row.number), ("Plan step", row.planStep)]) }}<td>{{ row.subject | inline_code }}</td><td>{% for file in row.files %}<code>{{ file }}</code>{% if not loop.last %} {% endif %}{% endfor %}</td></tr>{% endfor %}</tbody></table>{% endif %}
40
+ <table data-report-field="implementation.approvedPlanReference"><tbody><tr><th>{{ t('tasks.implementation.approved-plan') }}</th><td><code>{{ implementation.approvedPlanReference.planFile }}</code></td></tr><tr><th>{{ t('tasks.implementation.approval-evidence') }}</th><td>{{ implementation.approvedPlanReference.approvalEvidence | inline_code }}</td></tr><tr><th>{{ t('tasks.implementation.worktree') }}</th><td><code>{{ implementation.approvedPlanReference.executorWorktreePath }}</code> @ <code>{{ implementation.approvedPlanReference.baseRefSha }}</code></td></tr></tbody></table>
41
+ </section>
42
+
43
+ <section data-report-section="rollback" data-report-field="implementation.rollbackVerification">
44
+ <h2>{{ t('tasks.implementation.can-this-be-rolled-back') }}</h2>
45
+ <table><thead><tr><th>{{ t('tasks.implementation.subject') }}</th><th>{{ t('tasks.implementation.rollback-command') }}</th><th>{{ t('tasks.implementation.how-to-verify') }}</th><th>{{ t('tasks.implementation.result') }}</th></tr></thead><tbody>{% for row in implementation.rollbackVerification %}<tr><td>{{ row.category | inline_code }}</td><td><code>{{ row.rollbackCommand }}</code></td><td>{{ row.verification | inline_code }}</td><td>{{ row.result | inline_code }}</td></tr>{% else %}<tr><td colspan="4">{{ t('tasks.implementation.no-rollback-check-was-recorded') }}</td></tr>{% endfor %}</tbody></table>
46
+ {% if implementation.stageSidecarEvidence %}<p data-report-field="implementation.stageSidecarEvidence"><strong>{{ t('tasks.implementation.stage-carry-over') }}</strong> — Stage {{ implementation.stageSidecarEvidence.stageNumber }} {{ implementation.stageSidecarEvidence.stageTitle | inline_code }} · <code>{{ implementation.stageSidecarEvidence.carryJson }}</code></p>{% endif %}
34
47
  </section>
35
48
 
36
49
  <section data-report-section="manual-test" data-report-field="implementation.manualUserTest">
37
- <h2>수동 사용자 테스트</h2>
38
- {% if implementation.manualUserTest.applicable %}{% for row in implementation.manualUserTest["items"] %}<article class="summary-card"><h3>{{ row.target | inline_code }}</h3><p><strong>환경:</strong> {{ row.environmentSetup | inline_code }}</p><ol>{% for step in row.steps %}<li>{{ step | inline_code }}</li>{% endfor %}</ol><p><strong>기대 결과:</strong> {{ row.expectedResult | inline_code }}</p></article>{% endfor %}{% else %}<p>{{ implementation.manualUserTest.exemptionReason | inline_code }}</p>{% endif %}
50
+ <h2>{{ t('tasks.implementation.manual-user-test') }}</h2>
51
+ {% if implementation.manualUserTest.applicable %}{% for row in implementation.manualUserTest["items"] %}<article class="summary-card"><h3>{{ row.target | inline_code }}</h3><p><strong>{{ t('tasks.implementation.environment') }}</strong> {{ row.environmentSetup | inline_code }}</p><ol>{% for step in row.steps %}<li>{{ step | inline_code }}</li>{% endfor %}</ol><p><strong>{{ t('tasks.implementation.expected') }}</strong> {{ row.expectedResult | inline_code }}</p></article>{% endfor %}{% else %}<p>{{ implementation.manualUserTest.exemptionReason | inline_code }}</p>{% endif %}
52
+ </section>
53
+
54
+ <section data-report-section="next-step" data-report-field="implementation.routingRecommendation">
55
+ <h2>{{ t('tasks.implementation.next-step') }}</h2>
56
+ <p>{{ implementation.routingRecommendation | inline_code }}</p>
39
57
  </section>
40
58
  {% endblock %}
@@ -1,29 +1,29 @@
1
1
  {% extends "html/base.template.html" %}
2
- {% from "html/macros/layout.html" import narrative as render_narrative, summary_card %}
2
+ {% from "html/macros/layout.html" import narrative as render_narrative, summary_card, row_key %}
3
3
  {% from "html/macros/visualizations.html" import figure %}
4
4
 
5
5
  {% block human_content %}
6
6
  <section data-report-section="opportunities">
7
- <h2>발견한 개선 기회</h2>
8
- {{ render_narrative(narrative.opportunityOverview, "improvementDiscovery.userNarrative.opportunityOverview", evidenceIndex) }}
9
- <table data-report-field="improvementDiscovery.lensCoverage"><thead><tr><th>렌즈</th><th>결과</th><th>설명</th><th>근거</th></tr></thead><tbody>{% for row in improvement.lensCoverage %}<tr><td>{{ row.lens }}</td><td>{{ row.status }}</td><td>{{ row.rationale | inline_code }}</td><td>{{ row.evidence | evidence_refs(evidenceIndex) }}</td></tr>{% endfor %}</tbody></table>
7
+ <h2>{{ t('tasks.improvement-discovery.improvements-found') }}</h2>
8
+ {{ render_narrative(narrative.opportunityOverview, "improvementDiscovery.userNarrative.opportunityOverview") }}
9
+ <table data-report-field="improvementDiscovery.lensCoverage"><thead><tr><th>{{ t('tasks.improvement-discovery.lens') }}</th><th>{{ t('tasks.improvement-discovery.detail') }}</th><th>{{ t('tasks.improvement-discovery.evidence') }}</th></tr></thead><tbody>{% for row in improvement.lensCoverage %}<tr>{{ row_key(pairs=[("Lens", row.lens), ("Result", row.status | enum_label("lens"))]) }}<td>{{ row.rationale | inline_code }}</td><td>{{ row.evidence | evidence_refs }}</td></tr>{% endfor %}</tbody></table>
10
10
  </section>
11
11
 
12
12
  <section data-report-section="ranked-candidates">
13
- <h2>우선순위 후보</h2>
14
- {{ render_narrative(narrative.prioritizationExplanation, "improvementDiscovery.userNarrative.prioritizationExplanation", evidenceIndex) }}
15
- <div class="summary-grid" data-report-field="improvementDiscovery.candidates">{% for row in improvement.candidates %}<article class="summary-card tone-{{ row.severity }}"><p class="eyebrow">{{ loop.index }}순위 · {{ row.id }} · 영향 {{ row.severity }} · 노력 {{ row.effort }}</p><h3>{{ row.title | inline_code }}</h3><p>{{ row.expectedBehaviorAfter | inline_code }}</p><p><strong>다음 단계:</strong> {{ row.recommendedNextPhase }}</p><p class="evidence-refs">근거: {{ row.evidence | evidence_refs(evidenceIndex) }}</p></article>{% else %}<p>선택할 개선 후보가 없습니다.</p>{% endfor %}</div>
13
+ <h2>{{ t('tasks.improvement-discovery.ranked-candidates') }}</h2>
14
+ {{ render_narrative(narrative.prioritizationExplanation, "improvementDiscovery.userNarrative.prioritizationExplanation") }}
15
+ <div class="summary-grid" data-report-field="improvementDiscovery.candidates">{% for row in improvement.candidates %}<article class="summary-card tone-{{ row.severity }}" id="id-{{ row.id }}"><p class="eyebrow">#{{ loop.index }} · {{ row.id }} · Impact {{ row.severity }} · Effort {{ row.effort | enum_label("effort") }}</p><h3>{{ row.title | inline_code }}</h3><p>{{ row.expectedBehaviorAfter | inline_code }}</p><p><strong>{{ t('tasks.improvement-discovery.next-step') }}</strong> {{ row.recommendedNextPhase }}</p><p class="evidence-refs">Evidence: {{ row.evidence | evidence_refs }}</p></article>{% else %}<p>{{ t('tasks.improvement-discovery.there-is-no-improvement-candidate-to-pick') }}</p>{% endfor %}</div>
16
16
  </section>
17
17
 
18
18
  <section data-report-section="impact-effort">
19
- <h2>영향도와 노력도</h2>
19
+ <h2>{{ t('tasks.improvement-discovery.impact-and-effort') }}</h2>
20
20
  {{ figure(matrixFigure) }}
21
21
  </section>
22
22
 
23
23
  <section data-report-section="selection">
24
- <h2>선택 안내</h2>
25
- {{ render_narrative(narrative.selectionGuidance, "improvementDiscovery.userNarrative.selectionGuidance", evidenceIndex) }}
26
- <p><strong>선택 상한:</strong> 최대 {{ improvement.selectionLimit }}개</p>
24
+ <h2>{{ t('tasks.improvement-discovery.how-to-choose') }}</h2>
25
+ {{ render_narrative(narrative.selectionGuidance, "improvementDiscovery.userNarrative.selectionGuidance") }}
26
+ <p><strong>{{ t('tasks.improvement-discovery.selection-cap') }}</strong> at most {{ improvement.selectionLimit }}</p>
27
27
  <ul>{% for row in improvement.candidates %}<li><label><input type="checkbox" name="improvement-candidate" value="{{ row.id }}"> {{ row.id }} · {{ row.title | inline_code }}</label></li>{% endfor %}</ul>
28
28
  </section>
29
29
  {% endblock %}