okstra 0.148.0 → 0.149.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 (43) hide show
  1. package/package.json +1 -1
  2. package/runtime/BUILD.json +2 -2
  3. package/runtime/bin/okstra-render-report-views.py +20 -0
  4. package/runtime/prompts/profiles/project-analysis.md +18 -0
  5. package/runtime/prompts/profiles/release-handoff.md +3 -0
  6. package/runtime/prompts/profiles/requirements-discovery.md +7 -0
  7. package/runtime/python/okstra_ctl/report_html/common.py +77 -47
  8. package/runtime/python/okstra_ctl/report_html/filters.py +125 -30
  9. package/runtime/python/okstra_ctl/report_html/models.py +15 -0
  10. package/runtime/python/okstra_ctl/report_html/render.py +49 -5
  11. package/runtime/python/okstra_ctl/report_html/view_models/change_impact_analysis.py +14 -4
  12. package/runtime/python/okstra_ctl/report_html/view_models/error_analysis.py +3 -3
  13. package/runtime/python/okstra_ctl/report_html/view_models/feature_analysis.py +5 -3
  14. package/runtime/python/okstra_ctl/report_html/view_models/final_verification.py +2 -7
  15. package/runtime/python/okstra_ctl/report_html/view_models/implementation.py +5 -9
  16. package/runtime/python/okstra_ctl/report_html/view_models/implementation_planning.py +2 -5
  17. package/runtime/python/okstra_ctl/report_html/view_models/improvement_discovery.py +1 -2
  18. package/runtime/python/okstra_ctl/report_html/view_models/project_analysis.py +90 -4
  19. package/runtime/python/okstra_ctl/report_html/view_models/release_handoff.py +1 -8
  20. package/runtime/python/okstra_ctl/report_html/view_models/requirements_discovery.py +6 -4
  21. package/runtime/python/okstra_ctl/report_html/visualizations.py +146 -11
  22. package/runtime/python/okstra_ctl/report_view_artifacts.py +5 -0
  23. package/runtime/python/okstra_ctl/time_report.py +2 -2
  24. package/runtime/python/okstra_ctl/usage_report.py +2 -2
  25. package/runtime/schemas/final-report-v2.0.schema.json +229 -1
  26. package/runtime/templates/reports/final-report.template.md +55 -0
  27. package/runtime/templates/reports/html/assets/base.css +64 -8
  28. package/runtime/templates/reports/html/base.template.html +21 -41
  29. package/runtime/templates/reports/html/macros/forms.html +24 -22
  30. package/runtime/templates/reports/html/macros/layout.html +18 -5
  31. package/runtime/templates/reports/html/macros/visualizations.html +7 -5
  32. package/runtime/templates/reports/html/tasks/change-impact-analysis.template.html +30 -15
  33. package/runtime/templates/reports/html/tasks/error-analysis.template.html +22 -15
  34. package/runtime/templates/reports/html/tasks/feature-analysis.template.html +35 -15
  35. package/runtime/templates/reports/html/tasks/final-verification.template.html +21 -14
  36. package/runtime/templates/reports/html/tasks/implementation-planning.template.html +78 -19
  37. package/runtime/templates/reports/html/tasks/implementation.template.html +34 -16
  38. package/runtime/templates/reports/html/tasks/improvement-discovery.template.html +11 -11
  39. package/runtime/templates/reports/html/tasks/project-analysis.template.html +65 -25
  40. package/runtime/templates/reports/html/tasks/release-handoff.template.html +28 -14
  41. package/runtime/templates/reports/html/tasks/requirements-discovery.template.html +35 -15
  42. package/runtime/templates/reports/report.js +7 -1
  43. package/runtime/validators/validate_analysis_report.py +36 -0
@@ -1,7 +1,7 @@
1
- {% macro narrative(value, field_name, evidence_index={}) -%}
1
+ {% macro narrative(value, field_name) -%}
2
2
  <div class="narrative" data-report-field="{{ field_name }}">
3
3
  {{ value.text | paragraphs }}
4
- <p class="evidence-refs">근거: {{ value.evidenceRefs | evidence_refs(evidence_index) }}</p>
4
+ <p class="evidence-refs">Evidence: {{ value.evidenceRefs | evidence_refs }}</p>
5
5
  </div>
6
6
  {%- endmacro %}
7
7
 
@@ -11,9 +11,22 @@
11
11
  </ol>
12
12
  {%- endmacro %}
13
13
 
14
- {% macro summary_card(title, body, tone='neutral') -%}
15
- <article class="summary-card tone-{{ tone }}">
16
- <h3>{{ title }}</h3>
14
+ {# The values that identify a row — id, name, kind, owner, status — as one
15
+ cell, one labelled value per line. Given a column each they took roughly
16
+ half the table's width to carry a handful of characters, and the prose
17
+ columns beside them were squeezed to the point of stacking. Merging them
18
+ costs the column headings, so every value carries its own name here:
19
+ `pairs` is a list of (name, value), and the status keeps its pill. #}
20
+ {% macro row_key(pairs=[], status_name='', status_raw='', status_text='') -%}
21
+ <td class="row-key"><dl>
22
+ {%- for name, value in pairs %}{% if value %}<div><dt>{{ name }}</dt><dd>{{ value | inline_code }}</dd></div>{% endif %}{% endfor -%}
23
+ {%- if status_text %}<div><dt>{{ status_name }}</dt><dd><span class="status status-{{ status_raw }}">{{ status_text }}</span></dd></div>{% endif -%}
24
+ </dl></td>
25
+ {%- endmacro %}
26
+
27
+ {% macro summary_card(title, body, tone='neutral', anchor='') -%}
28
+ <article class="summary-card tone-{{ tone }}"{% if anchor %} id="id-{{ anchor }}"{% endif %}>
29
+ {% if title %}<h3>{{ title }}</h3>{% endif %}
17
30
  <p>{{ body | inline_code }}</p>
18
31
  </article>
19
32
  {%- endmacro %}
@@ -1,24 +1,26 @@
1
- {% macro figure(model) -%}
1
+ {% from "html/macros/layout.html" import row_key %}
2
+ {% macro figure(model, anchor_nodes=false) -%}
2
3
  <figure data-figure-kind="{{ model.kind }}" aria-labelledby="{{ model.figure_id }}-title" aria-describedby="{{ model.figure_id }}-summary">
3
4
  <figcaption>
4
5
  <strong id="{{ model.figure_id }}-title">{{ model.title }}</strong>
5
6
  <span id="{{ model.figure_id }}-summary">{{ model.summary }}</span>
6
7
  </figcaption>
7
8
  <div class="visualization" aria-hidden="true">{{ model.svg | safe }}</div>
9
+ {% set show_paths = model.nodes | selectattr("paths") | first is defined %}
8
10
  <table class="visualization-fallback">
9
- <thead><tr><th>ID</th><th>이름</th><th>그룹</th><th>상태</th><th>설명</th></tr></thead>
11
+ <thead><tr><th>Component</th><th>What it does</th>{% if show_paths %}<th>Paths</th>{% endif %}</tr></thead>
10
12
  <tbody>
11
13
  {% for node in model.nodes %}
12
- <tr data-fallback-id="{{ node.id }}"><td>{{ node.id }}</td><td>{{ node.label | inline_code }}</td><td>{{ node.group | inline_code }}</td><td>{{ node.status }}</td><td>{{ node.detail | inline_code }}</td></tr>
14
+ <tr{% if anchor_nodes %} id="id-{{ node.id }}"{% endif %} data-fallback-id="{{ node.id }}">{{ row_key(pairs=[("ID", node.id), ("Name", node.label), ("Kind", node.note)]) }}<td>{{ node.detail | inline_code }}</td>{% if show_paths %}<td>{% for path in node.paths %}<code>{{ path }}</code>{% if not loop.last %} {% endif %}{% endfor %}</td>{% endif %}</tr>
13
15
  {% endfor %}
14
16
  </tbody>
15
17
  </table>
16
18
  {% if model.edges %}
17
19
  <table class="visualization-fallback visualization-edges">
18
- <thead><tr><th>출발</th><th>도착</th><th>관계</th></tr></thead>
20
+ <thead><tr><th>From</th><th>To</th><th>Relation</th></tr></thead>
19
21
  <tbody>
20
22
  {% for edge in model.edges %}
21
- <tr data-fallback-edge="{{ edge.source }}-{{ edge.target }}"><td>{{ edge.source }}</td><td>{{ edge.target }}</td><td>{{ edge.label | inline_code }}</td></tr>
23
+ <tr data-fallback-edge="{{ edge.source }}-{{ edge.target }}"><td>{{ edge.source | inline_code }}</td><td>{{ edge.target | inline_code }}</td><td>{{ edge.label | inline_code }}</td></tr>
22
24
  {% endfor %}
23
25
  </tbody>
24
26
  </table>
@@ -5,35 +5,50 @@
5
5
 
6
6
  {% block human_content %}
7
7
  <section data-report-section="change-overview">
8
- <h2>변경 개요</h2>
9
- {{ render_narrative(narrative.changeExplanation, "changeImpactAnalysis.userNarrative.changeExplanation", evidenceIndex) }}
8
+ <h2>Change overview</h2>
9
+ {{ render_narrative(narrative.changeExplanation, "changeImpactAnalysis.userNarrative.changeExplanation") }}
10
10
  <p>{{ change.changeRequest.summary | inline_code }}</p>
11
11
  </section>
12
12
 
13
13
  <section data-report-section="affected-and-unaffected">
14
- <h2>영향받는 영역과 영향 없는 경계</h2>
15
- {{ render_narrative(narrative.impactExplanation, "changeImpactAnalysis.userNarrative.impactExplanation", evidenceIndex) }}
16
- <div data-report-field="changeImpactAnalysis.impactItems"><h3>영향받는 영역</h3>{% for row in change.impactItems %}{{ summary_card(row.id ~ " · " ~ (row.target | inline_code), row.impactKind ~ " / 영향 " ~ row.level ~ " / 신뢰 " ~ row.confidence) }}{% endfor %}</div>
17
- <div data-report-field="changeImpactAnalysis.unaffectedBoundaries"><h3>영향 없는 경계</h3>{{ render_narrative(narrative.unaffectedExplanation, "changeImpactAnalysis.userNarrative.unaffectedExplanation", evidenceIndex) }}<ul>{% for row in change.unaffectedBoundaries %}<li>{{ row.summary | inline_code }}</li>{% endfor %}</ul></div>
14
+ <h2>What the change touches, and where it stops</h2>
15
+ {{ render_narrative(narrative.impactExplanation, "changeImpactAnalysis.userNarrative.impactExplanation") }}
16
+ <div data-report-field="changeImpactAnalysis.impactItems"><h3>What it touches</h3>{% for row in change.impactItems %}{{ summary_card(row.id ~ " · " ~ (row.target | inline_code), row.impactKind ~ " / Impact " ~ row.level ~ " / Confidence " ~ row.confidence, anchor=row.id) }}{% endfor %}</div>
17
+ <div data-report-field="changeImpactAnalysis.unaffectedBoundaries"><h3>Where the change stops</h3>{{ render_narrative(narrative.unaffectedExplanation, "changeImpactAnalysis.userNarrative.unaffectedExplanation") }}<ul>{% for row in change.unaffectedBoundaries %}<li>{{ row.summary | inline_code }}</li>{% endfor %}</ul></div>
18
18
  </section>
19
19
 
20
20
  <section data-report-section="blast-radius" data-report-field="changeImpactAnalysis.dependencyBlastRadius">
21
- <h2>파급 범위</h2>
21
+ <h2>Blast radius</h2>
22
22
  {{ figure(blastRadiusFigure) }}
23
23
  </section>
24
24
 
25
25
  <section data-report-section="test-and-operations">
26
- <h2>테스트·운영·호환성</h2>
27
- <div data-report-field="changeImpactAnalysis.compatibilityImpact"><h3>호환성</h3><ul>{% for row in change.compatibilityImpact %}<li>{{ row.summary | inline_code }}</li>{% endfor %}</ul></div>
28
- <div data-report-field="changeImpactAnalysis.migrationImpact"><h3>마이그레이션</h3><ul>{% for row in change.migrationImpact %}<li>{{ row.summary | inline_code }}</li>{% endfor %}</ul></div>
29
- <div data-report-field="changeImpactAnalysis.rollbackImpact"><h3>롤백</h3><ul>{% for row in change.rollbackImpact %}<li>{{ row.summary | inline_code }}</li>{% endfor %}</ul></div>
30
- <div data-report-field="changeImpactAnalysis.securityAndPerformanceImpact"><h3>보안과 성능</h3><ul>{% for row in change.securityAndPerformanceImpact %}<li>{{ row.summary | inline_code }}</li>{% endfor %}</ul></div>
26
+ <h2>Tests, operations, compatibility</h2>
27
+ <div data-report-field="changeImpactAnalysis.compatibilityImpact"><h3>Compatibility</h3><ul>{% for row in change.compatibilityImpact %}<li>{{ row.summary | inline_code }}</li>{% endfor %}</ul></div>
28
+ <div data-report-field="changeImpactAnalysis.migrationImpact"><h3>Migration</h3><ul>{% for row in change.migrationImpact %}<li>{{ row.summary | inline_code }}</li>{% endfor %}</ul></div>
29
+ <div data-report-field="changeImpactAnalysis.rollbackImpact"><h3>Rollback</h3><ul>{% for row in change.rollbackImpact %}<li>{{ row.summary | inline_code }}</li>{% endfor %}</ul></div>
30
+ <div data-report-field="changeImpactAnalysis.securityAndPerformanceImpact"><h3>Security and performance</h3><ul>{% for row in change.securityAndPerformanceImpact %}<li>{{ row.summary | inline_code }}</li>{% endfor %}</ul></div>
31
31
  </section>
32
32
 
33
33
  <section data-report-section="planning-readiness" data-report-field="changeImpactAnalysis.planningInputs">
34
- <h2>계획 준비도</h2>
35
- {{ render_narrative(narrative.planningReadiness, "changeImpactAnalysis.userNarrative.planningReadiness", evidenceIndex) }}
36
- <ul>{% for row in change.planningInputs %}<li>{{ row.constraint | inline_code }}{% if row.unknown %} — 미확정{% endif %}</li>{% endfor %}</ul>
34
+ <h2>Plan readiness</h2>
35
+ {{ render_narrative(narrative.planningReadiness, "changeImpactAnalysis.userNarrative.planningReadiness") }}
36
+ <ul>{% for row in change.planningInputs %}<li>{{ row.constraint | inline_code }}{% if row.unknown %} — Unsettled{% endif %}</li>{% endfor %}</ul>
37
+ </section>
38
+
39
+ <section data-report-section="preserved-behaviors" data-report-field="changeImpactAnalysis.preservedBehaviors">
40
+ <h2>Behaviour that must not change</h2>
41
+ <table><thead><tr><th>Body</th><th>Evidence</th></tr></thead><tbody>{% for row in change.preservedBehaviors %}<tr><td>{{ row.statement | inline_code }}</td><td>{{ row.currentCodeEvidence | code_evidence }}</td></tr>{% else %}<tr><td colspan="2">No behaviour is pinned.</td></tr>{% endfor %}</tbody></table>
42
+ </section>
43
+
44
+ <section data-report-section="test-impact" data-report-field="changeImpactAnalysis.testImpact">
45
+ <h2>What this does to the tests</h2>
46
+ <table><thead><tr><th>Scope</th><th>Action needed</th><th>Tests targeted</th><th>Evidence</th></tr></thead><tbody>{% for row in change.testImpact %}<tr><td>{{ row.scope | inline_code }}</td><td>{{ row.action | inline_code }}</td><td>{% for path in row.testPaths %}<code>{{ path }}</code>{% if not loop.last %} {% endif %}{% endfor %}</td><td>{{ row.currentCodeEvidence | code_evidence }}</td></tr>{% else %}<tr><td colspan="4">No test is affected.</td></tr>{% endfor %}</tbody></table>
47
+ </section>
48
+
49
+ <section data-report-section="operational-impact" data-report-field="changeImpactAnalysis.operationalImpact">
50
+ <h2>What this does to operations</h2>
51
+ <table><thead><tr><th>Area</th><th>Impact</th><th>Evidence</th></tr></thead><tbody>{% for row in change.operationalImpact %}<tr><td>{{ row.area | inline_code }}</td><td>{{ row.impact | inline_code }}</td><td>{{ row.currentCodeEvidence | code_evidence }}</td></tr>{% else %}<tr><td colspan="3">There is no operational impact.</td></tr>{% endfor %}</tbody></table>
37
52
  </section>
38
53
 
39
54
  {{ analysis_review(analysisReviewIds) }}
@@ -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>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>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>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>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>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>Confidence now:</strong> {{ row.confidence }}</p><p data-report-field="errorAnalysis.causeCandidates.falsifyingEvidenceChecked"><strong>Disproof attempted:</strong> {{ row.falsifyingEvidenceChecked | join(', ') | inline_code }}</p><p data-report-field="errorAnalysis.causeCandidates.disproveWith"><strong>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>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>Signal to check:</strong> {{ error.nextDiagnostic.confirmingSignal | inline_code }}</p>
38
+ <p><strong>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>Next step</h2>
43
+ <p><strong>Recommended task</strong> — <code>{{ error.routing.nextTaskType }}</code></p>
44
+ <p><strong>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>Feature overview</h2>
9
+ {{ render_narrative(narrative.userBehaviorExplanation, "featureAnalysis.userNarrative.userBehaviorExplanation") }}
10
+ <p><strong>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>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>Rules and state changes</h2>
23
+ {{ render_narrative(narrative.rulesAndStateExplanation, "featureAnalysis.userNarrative.rulesAndStateExplanation") }}
24
+ <div data-report-field="featureAnalysis.domainRules"><h3>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>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>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>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>Precondition</h2>
41
+ <table><thead><tr><th>Body</th><th>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">Nothing was recorded.</td></tr>{% endfor %}</tbody></table>
42
+ </section>
43
+
44
+ <section data-report-section="postconditions" data-report-field="featureAnalysis.postconditions">
45
+ <h2>State once it is done</h2>
46
+ <table><thead><tr><th>Body</th><th>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">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>Permissions and flags</h2>
51
+ <table><thead><tr><th>Body</th><th>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">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>Non-functional concerns</h2>
56
+ <table><thead><tr><th>Body</th><th>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">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>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>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>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>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>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>Owner:</strong> {{ row.owner | inline_code }}</p><p><strong>Escalation condition:</strong> {{ row.escalationTrigger | inline_code }}</p></article>{% else %}<p>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>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>What was verified</h2>
36
+ <table><tbody><tr><th>Implementation report</th><td><code>{{ final.sourceImplementationReport.path }}</code></td></tr><tr><th>Worktree</th><td><code>{{ final.sourceImplementationReport.worktreePath }}</code></td></tr><tr><th>Base commit</th><td><code>{{ final.sourceImplementationReport.implementationBaseRef }}</code> → <code>{{ final.sourceImplementationReport.capturedHeadSha }}</code></td></tr><tr><th>Diff summary</th><td>{{ final.sourceImplementationReport.diffSummaryQuote | inline_code }}</td></tr><tr><th>Uncommitted state</th><td>{{ final.sourceImplementationReport.gitStatusShort | inline_code }}</td></tr></tbody></table>
37
+ {% if final.get("stageReports") %}<h3>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>Release path</h2>
43
+ {{ render_narrative(narrative.releaseReadiness, "finalVerification.userNarrative.releaseReadiness") }}
44
+ {% if releaseAllowed %}<p>{{ final.routingRecommendation | inline_code }}</p>{% else %}<p>The current verdict does not allow release-handoff to start. Clear the blocker or the condition first.</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>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>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>Interface:</strong> {{ row.interfaces | inline_code }}</p><p><strong>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>Option</th><th>Complexity</th><th>Risk</th><th>Reversibility</th><th>Test cost</th><th>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>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>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>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>Exit contract:</strong> {{ row.acceptance | inline_code }}</p><p><strong>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>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>Design item</th><th>What it needs</th><th>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>Where implementations diverge</h2>
46
+ {% if planning.variationPointAnalysis.hasMultipleImplementations %}
47
+ <table><thead><tr><th>Action</th><th>Implementations</th><th>Extraction call</th><th>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>Step-by-step execution</h2>
54
+ <table><thead><tr><th>#</th><th>Action</th><th>Files</th><th>Command / test</th><th>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>Verification and rollback</h2>
60
+ {{ render_narrative(narrative.validationAndRollback, "implementationPlanning.userNarrative.validationAndRollback") }}
61
+ <h3>Validation checklist</h3><table data-report-field="implementationPlanning.validationChecklist"><thead><tr><th>Check</th><th>Check</th><th>Command</th><th>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>How each requirement gets met</h3><table data-report-field="implementationPlanning.requirementCoverage"><thead><tr><th>Requirement</th><th>Body</th><th>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>Cross-project dependencies</h3><table data-report-field="implementationPlanning.crossProjectDependencies"><thead><tr><th>Dependency</th><th>Work needed</th><th>Signal to check</th><th>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>Dependency and migration risk</h3><table data-report-field="implementationPlanning.dependencyMigrationRisk"><thead><tr><th>Item</th><th>Impact</th><th>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>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>Plan body verification</h2>
70
+ <p><strong>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>What it blocked</strong> — {{ planning.planBodyVerification.gateBlockedBy | join(", ") | inline_code }}</p>{% endif %}
72
+ <table><thead><tr><th>Plan item</th><th>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>Dissent left standing</h3>
74
+ <table><thead><tr><th>Subject</th><th>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>What your answers overturned</h2>
80
+ <table><thead><tr><th>Answer</th><th>Statement overturned</th><th>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">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>Scope, restated</h2>
86
+ <p><strong>{{ planning.incrementalDecision.mode | inline_code }}</strong> — {{ planning.incrementalDecision.reason | inline_code }}</p>
87
+ <p><strong>Stages verified again</strong> — {{ planning.incrementalDecision.reverifyStages | default([]) | join(", ") | inline_code }} · <strong>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>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>Context</strong> — {{ row.context | inline_code }}</p><p><strong>Decision</strong> — {{ row.decision | inline_code }}</p><p><strong>Result</strong> — {{ row.consequences | inline_code }}</p></article>
94
+ {% else %}<p>There is no decision draft to record.</p>{% endfor %}
95
+ {% if planning.get("skippedAdrCandidates") %}<h3>What was deliberately not recorded</h3>
96
+ <table data-report-field="implementationPlanning.skippedAdrCandidates"><thead><tr><th>Topic</th><th>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>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>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>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>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>Requirement coverage</h2>
20
+ <table data-report-field="implementation.requirementCoverage"><thead><tr><th>Requirement</th><th>Body</th><th>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>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>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>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>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>Commits delivered</h2>
38
+ {% if implementation.commitList is mapping %}<p>There are no commits.</p>{% else %}
39
+ <table><thead><tr><th>Commit</th><th>Subject</th><th>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>Approved plan</th><td><code>{{ implementation.approvedPlanReference.planFile }}</code></td></tr><tr><th>Approval evidence</th><td>{{ implementation.approvedPlanReference.approvalEvidence | inline_code }}</td></tr><tr><th>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>Can this be rolled back</h2>
45
+ <table><thead><tr><th>Subject</th><th>Rollback command</th><th>How to verify</th><th>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">No rollback check was recorded.</td></tr>{% endfor %}</tbody></table>
46
+ {% if implementation.stageSidecarEvidence %}<p data-report-field="implementation.stageSidecarEvidence"><strong>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>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>Environment:</strong> {{ row.environmentSetup | inline_code }}</p><ol>{% for step in row.steps %}<li>{{ step | inline_code }}</li>{% endfor %}</ol><p><strong>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>Next step</h2>
56
+ <p>{{ implementation.routingRecommendation | inline_code }}</p>
39
57
  </section>
40
58
  {% endblock %}