okstra 0.146.0 → 0.147.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/docs/architecture/storage-model.md +8 -7
- package/docs/architecture.md +18 -12
- package/docs/cli.md +3 -3
- package/docs/project-structure-overview.md +16 -14
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/workers/report-writer-worker.md +10 -10
- package/runtime/bin/okstra-render-final-report.py +4 -4
- package/runtime/bin/okstra-render-report-views.py +100 -12
- package/runtime/bin/okstra-trace-cleanup.sh +13 -9
- package/runtime/prompts/lead/okstra-lead-contract.md +4 -4
- package/runtime/prompts/lead/report-writer.md +15 -11
- package/runtime/prompts/profiles/_common-contract.md +13 -7
- package/runtime/prompts/profiles/improvement-discovery.md +3 -1
- package/runtime/python/okstra_ctl/final_report_schema.py +37 -12
- package/runtime/python/okstra_ctl/render_final_report.py +136 -28
- package/runtime/python/okstra_ctl/report_contract.py +124 -0
- package/runtime/python/okstra_ctl/report_finalize.py +1 -1
- package/runtime/python/okstra_ctl/report_html/__init__.py +10 -0
- package/runtime/python/okstra_ctl/report_html/common.py +50 -0
- package/runtime/python/okstra_ctl/report_html/models.py +59 -0
- package/runtime/python/okstra_ctl/report_html/render.py +69 -0
- package/runtime/python/okstra_ctl/report_html/router.py +40 -0
- package/runtime/python/okstra_ctl/report_html/view_models/__init__.py +1 -0
- package/runtime/python/okstra_ctl/report_html/view_models/change_impact_analysis.py +38 -0
- package/runtime/python/okstra_ctl/report_html/view_models/error_analysis.py +48 -0
- package/runtime/python/okstra_ctl/report_html/view_models/feature_analysis.py +38 -0
- package/runtime/python/okstra_ctl/report_html/view_models/final_verification.py +46 -0
- package/runtime/python/okstra_ctl/report_html/view_models/implementation.py +46 -0
- package/runtime/python/okstra_ctl/report_html/view_models/implementation_planning.py +102 -0
- package/runtime/python/okstra_ctl/report_html/view_models/improvement_discovery.py +42 -0
- package/runtime/python/okstra_ctl/report_html/view_models/project_analysis.py +54 -0
- package/runtime/python/okstra_ctl/report_html/view_models/release_handoff.py +53 -0
- package/runtime/python/okstra_ctl/report_html/view_models/requirements_discovery.py +54 -0
- package/runtime/python/okstra_ctl/report_html/visualizations.py +113 -0
- package/runtime/python/okstra_ctl/report_view_artifacts.py +4 -1
- package/runtime/python/okstra_ctl/report_views.py +15 -43
- package/runtime/python/okstra_ctl/run.py +12 -6
- package/runtime/python/okstra_ctl/schema_excerpt.py +7 -17
- package/runtime/schemas/final-report-v2.0.schema.json +3923 -0
- package/runtime/templates/reports/final-report-v2.template.md +66 -0
- package/runtime/templates/reports/html/assets/base.css +38 -0
- package/runtime/templates/reports/html/assets/base.js +5 -0
- package/runtime/templates/reports/html/base.template.html +65 -0
- package/runtime/templates/reports/html/macros/forms.html +47 -0
- package/runtime/templates/reports/html/macros/layout.html +19 -0
- package/runtime/templates/reports/html/macros/visualizations.html +17 -0
- package/runtime/templates/reports/html/tasks/change-impact-analysis.template.html +40 -0
- package/runtime/templates/reports/html/tasks/error-analysis.template.html +40 -0
- package/runtime/templates/reports/html/tasks/feature-analysis.template.html +40 -0
- package/runtime/templates/reports/html/tasks/final-verification.template.html +39 -0
- package/runtime/templates/reports/html/tasks/implementation-planning.template.html +47 -0
- package/runtime/templates/reports/html/tasks/implementation.template.html +40 -0
- package/runtime/templates/reports/html/tasks/improvement-discovery.template.html +29 -0
- package/runtime/templates/reports/html/tasks/project-analysis.template.html +57 -0
- package/runtime/templates/reports/html/tasks/release-handoff.template.html +36 -0
- package/runtime/templates/reports/html/tasks/requirements-discovery.template.html +37 -0
- package/runtime/validators/validate-report-views.py +86 -4
- package/runtime/validators/validate-run.py +62 -9
- package/runtime/validators/validate_improvement_report.py +55 -0
- package/src/commands/lifecycle/install.mjs +18 -13
- package/src/commands/report/finalize.mjs +2 -3
- package/src/commands/report/render-final-report.mjs +4 -2
- package/src/commands/report/render-views.mjs +8 -8
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: {{ frontmatter.title | yaml_scalar }}
|
|
3
|
+
id: {{ frontmatter.id | yaml_scalar }}
|
|
4
|
+
tags: {{ frontmatter.tags | yaml_inline_list }}
|
|
5
|
+
status: {{ frontmatter.status | yaml_scalar }}
|
|
6
|
+
aliases: {{ frontmatter.aliases | yaml_inline_list }}
|
|
7
|
+
date: {{ frontmatter.date | yaml_scalar }}
|
|
8
|
+
task-id: {{ frontmatter.taskId | yaml_scalar }}
|
|
9
|
+
task-group: {{ frontmatter.taskGroup | yaml_scalar }}
|
|
10
|
+
project-id: {{ frontmatter.projectId | yaml_scalar }}
|
|
11
|
+
task-type: {{ frontmatter.taskType | yaml_scalar }}
|
|
12
|
+
worker-id: {{ frontmatter.workerId | yaml_scalar }}
|
|
13
|
+
approved: {{ "true" if frontmatter.approved else "false" }}
|
|
14
|
+
implementation-option: {{ frontmatter.implementationOption | default("") | yaml_scalar }}
|
|
15
|
+
schema-version: {{ schemaVersion | yaml_scalar }}
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
# {{ frontmatter.title }}
|
|
19
|
+
|
|
20
|
+
- Task Key: `{{ header.taskKey }}`
|
|
21
|
+
- Created At: `{{ header.createdAt }}`
|
|
22
|
+
- Task Type: `{{ header.taskType }}`
|
|
23
|
+
- Report Owner: `{{ header.reportOwner }}`
|
|
24
|
+
- Report Author: `{{ header.reportAuthor }}`
|
|
25
|
+
- Lead Model: `{{ header.leadModel | model_detail }}`
|
|
26
|
+
- Okstra Version: `{{ header.okstraVersion }}`
|
|
27
|
+
|
|
28
|
+
## AI Handoff Summary
|
|
29
|
+
|
|
30
|
+
- Verdict: `{{ finalVerdict.verdictToken }}`
|
|
31
|
+
- Direction: `{{ finalVerdict.direction }}`
|
|
32
|
+
- Rationale IDs: {{ finalVerdict.rationaleRowIds | join(", ") }}
|
|
33
|
+
- Blocking IDs: {{ aiBlockingIds | join(", ") if aiBlockingIds else "none" }}
|
|
34
|
+
- Next step: {{ finalVerdict.nextStep }}
|
|
35
|
+
|
|
36
|
+
### Decision Context
|
|
37
|
+
|
|
38
|
+
{{ aiDecisionContext | json_block }}
|
|
39
|
+
|
|
40
|
+
### Next Task Contract
|
|
41
|
+
|
|
42
|
+
{{ aiRoutingContract | json_block }}
|
|
43
|
+
|
|
44
|
+
## Clarification and User Decisions
|
|
45
|
+
|
|
46
|
+
{{ clarificationItems | json_block }}
|
|
47
|
+
|
|
48
|
+
## Evidence Ledger
|
|
49
|
+
|
|
50
|
+
{{ aiEvidenceLedger | json_block }}
|
|
51
|
+
|
|
52
|
+
## Task Deliverable: {{ aiTaskDeliverableTitle }}
|
|
53
|
+
|
|
54
|
+
{{ aiTaskDeliverable | json_block }}
|
|
55
|
+
|
|
56
|
+
## Cross Verification Audit
|
|
57
|
+
|
|
58
|
+
{{ crossVerification | json_block }}
|
|
59
|
+
|
|
60
|
+
## Execution Audit
|
|
61
|
+
|
|
62
|
+
{{ executionStatus | json_block }}
|
|
63
|
+
|
|
64
|
+
## Token and Cost Audit
|
|
65
|
+
|
|
66
|
+
{{ tokenUsage | json_block }}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
* { box-sizing: border-box; }
|
|
2
|
+
:root { color-scheme: light dark; font-family: Inter, ui-sans-serif, system-ui, sans-serif; line-height: 1.55; }
|
|
3
|
+
body { margin: 0; background: Canvas; color: CanvasText; }
|
|
4
|
+
.skip-link { position: absolute; left: -999px; }
|
|
5
|
+
.skip-link:focus { left: 1rem; top: 1rem; z-index: 20; background: Canvas; padding: .6rem; }
|
|
6
|
+
.human-report-header, main, .audit { width: min(1120px, calc(100% - 2rem)); margin-inline: auto; }
|
|
7
|
+
.human-report-header { padding: 3rem 0 1.5rem; }
|
|
8
|
+
.human-report-header h1 { max-width: 22ch; font-size: clamp(2rem, 6vw, 4rem); line-height: 1.05; margin: .25rem 0 1rem; }
|
|
9
|
+
.eyebrow { text-transform: uppercase; letter-spacing: .08em; font-size: .78rem; color: GrayText; }
|
|
10
|
+
main { display: grid; gap: 1rem; padding-bottom: 3rem; }
|
|
11
|
+
section { padding: 1.4rem; border: 1px solid color-mix(in srgb, CanvasText 14%, transparent); border-radius: 16px; background: color-mix(in srgb, Canvas 94%, CanvasText 6%); }
|
|
12
|
+
h2 { margin-top: 0; font-size: 1.45rem; }
|
|
13
|
+
h3 { margin-bottom: .35rem; }
|
|
14
|
+
.summary-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: .8rem; }
|
|
15
|
+
.summary-card { padding: 1rem; border-radius: 12px; background: color-mix(in srgb, Highlight 8%, Canvas); border-left: 4px solid color-mix(in srgb, Highlight 55%, GrayText); }
|
|
16
|
+
.tone-high, .tone-important { border-left-color: #d94b4b; }
|
|
17
|
+
.tone-medium { border-left-color: #d79b25; }
|
|
18
|
+
.evidence-refs { color: GrayText; font-size: .85rem; }
|
|
19
|
+
figure { margin: 1rem 0 0; }
|
|
20
|
+
figcaption { display: flex; flex-wrap: wrap; justify-content: space-between; gap: .5rem; margin-bottom: .7rem; }
|
|
21
|
+
.visualization { overflow-x: auto; border-radius: 12px; background: color-mix(in srgb, CanvasText 4%, Canvas); }
|
|
22
|
+
svg { width: 100%; min-width: 520px; height: auto; }
|
|
23
|
+
svg .edge { stroke: GrayText; stroke-width: 2; }
|
|
24
|
+
svg .node rect { fill: color-mix(in srgb, Highlight 14%, Canvas); stroke: Highlight; }
|
|
25
|
+
svg .node text { fill: CanvasText; font-size: 13px; }
|
|
26
|
+
table { width: 100%; border-collapse: collapse; margin-top: 1rem; font-size: .9rem; }
|
|
27
|
+
th, td { text-align: left; vertical-align: top; border-bottom: 1px solid color-mix(in srgb, CanvasText 18%, transparent); padding: .55rem; }
|
|
28
|
+
.visualization-fallback { margin-top: .8rem; }
|
|
29
|
+
.status { display: inline-block; border-radius: 999px; padding: .1rem .5rem; background: color-mix(in srgb, GrayText 15%, Canvas); }
|
|
30
|
+
.status-gap, .status-risk { background: color-mix(in srgb, #d94b4b 18%, Canvas); }
|
|
31
|
+
.audit { margin-bottom: 3rem; padding: 1rem; border: 1px solid GrayText; border-radius: 12px; }
|
|
32
|
+
.audit pre { overflow: auto; white-space: pre-wrap; }
|
|
33
|
+
.human-report-footer { width: min(1120px, calc(100% - 2rem)); margin: 0 auto 2rem; display: flex; flex-wrap: wrap; gap: .6rem; }
|
|
34
|
+
.human-report-footer pre { flex-basis: 100%; white-space: pre-wrap; }
|
|
35
|
+
fieldset, label { display: block; margin: .7rem 0; }
|
|
36
|
+
textarea, select { width: 100%; max-width: 60rem; padding: .55rem; font: inherit; }
|
|
37
|
+
@media (max-width: 640px) { section { padding: 1rem; } .visualization { display: none; } }
|
|
38
|
+
@media print { .skip-link, .audit, script { display: none !important; } body { color: #000; background: #fff; } section { break-inside: avoid; border-color: #bbb; } .visualization-fallback { display: table; } }
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
{% from "html/macros/forms.html" import clarification_responses %}
|
|
3
|
+
<html lang="{{ 'ko' if humanSummary else 'en' }}" data-task-template="{{ taskType }}">
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="utf-8">
|
|
6
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
7
|
+
<title>{{ humanSummary.headline }}</title>
|
|
8
|
+
<style>{{ css | safe }}</style>
|
|
9
|
+
</head>
|
|
10
|
+
<body>
|
|
11
|
+
<a class="skip-link" href="#main-content">Skip to report content</a>
|
|
12
|
+
<header class="human-report-header">
|
|
13
|
+
<p class="eyebrow">{{ taskType }}</p>
|
|
14
|
+
<h1>{{ humanSummary.headline }}</h1>
|
|
15
|
+
<p>{{ humanSummary.outcome }}</p>
|
|
16
|
+
<button type="button" data-action="export-user-response">사용자 응답 내보내기</button>
|
|
17
|
+
</header>
|
|
18
|
+
<main id="main-content" data-report-role="human-main">
|
|
19
|
+
{% block human_content %}{% endblock %}
|
|
20
|
+
{{ clarification_responses(clarificationItems) }}
|
|
21
|
+
</main>
|
|
22
|
+
<details id="audit-details" class="audit" data-report-role="audit">
|
|
23
|
+
<summary>감사 정보</summary>
|
|
24
|
+
<section data-audit-field="crossVerification">
|
|
25
|
+
<h2>Cross Verification</h2>
|
|
26
|
+
<pre>{{ audit.crossVerification | tojson(indent=2) }}</pre>
|
|
27
|
+
</section>
|
|
28
|
+
<section data-audit-field="executionStatus">
|
|
29
|
+
<h2>Execution Status by Agent</h2>
|
|
30
|
+
<pre>{{ audit.executionStatus | tojson(indent=2) }}</pre>
|
|
31
|
+
</section>
|
|
32
|
+
<section data-audit-field="tokenUsage">
|
|
33
|
+
<h2>Token Usage</h2>
|
|
34
|
+
<pre>{{ audit.tokenUsage | tojson(indent=2) }}</pre>
|
|
35
|
+
</section>
|
|
36
|
+
{% if audit.planBodyVerification is defined %}
|
|
37
|
+
<section data-audit-field="planBodyVerification">
|
|
38
|
+
<h2>Plan Body Verification</h2>
|
|
39
|
+
<pre>{{ audit.planBodyVerification | tojson(indent=2) }}</pre>
|
|
40
|
+
</section>
|
|
41
|
+
{% endif %}
|
|
42
|
+
{% if audit.deliveryEvidence is defined %}
|
|
43
|
+
<section data-audit-field="deliveryEvidence">
|
|
44
|
+
<h2>Delivery Evidence</h2>
|
|
45
|
+
<pre>{{ audit.deliveryEvidence | tojson(indent=2) }}</pre>
|
|
46
|
+
</section>
|
|
47
|
+
{% endif %}
|
|
48
|
+
</details>
|
|
49
|
+
<footer class="human-report-footer">
|
|
50
|
+
<button type="button" data-action="export-user-response">사용자 응답 내보내기</button>
|
|
51
|
+
<button type="button" data-action="copy-user-response">복사</button>
|
|
52
|
+
<button type="button" data-action="dismiss-user-response" hidden>닫기</button>
|
|
53
|
+
<pre id="user-response-output" aria-live="polite"></pre>
|
|
54
|
+
</footer>
|
|
55
|
+
<script id="run-meta" type="application/json">{{ {
|
|
56
|
+
"task-key": runMeta.task_key,
|
|
57
|
+
"task-type": runMeta.task_type,
|
|
58
|
+
"seq": runMeta.seq,
|
|
59
|
+
"source-report": runMeta.source_report,
|
|
60
|
+
"source-data-sha256": dataSha256,
|
|
61
|
+
"source-md-sha256": markdownSha256
|
|
62
|
+
} | tojson }}</script>
|
|
63
|
+
<script>{{ js | safe }}</script>
|
|
64
|
+
</body>
|
|
65
|
+
</html>
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{% macro analysis_review(selector_ids) -%}
|
|
2
|
+
<section id="analysis-review" data-report-section="analysis-review">
|
|
3
|
+
<h2>분석 검토</h2>
|
|
4
|
+
<fieldset><legend>판정</legend>
|
|
5
|
+
<input id="analysis-review-accepted" type="radio" name="analysis-review-status" value="accepted"><label for="analysis-review-accepted">수락</label>
|
|
6
|
+
<input id="analysis-review-revision-requested" type="radio" name="analysis-review-status" value="revision-requested"><label for="analysis-review-revision-requested">수정 요청</label>
|
|
7
|
+
<input id="analysis-review-rejected" type="radio" name="analysis-review-status" value="rejected"><label for="analysis-review-rejected">거절</label>
|
|
8
|
+
</fieldset>
|
|
9
|
+
<label for="analysis-review-affected-ids">영향받는 ID</label>
|
|
10
|
+
<select id="analysis-review-affected-ids" multiple>
|
|
11
|
+
{% for analysis_id in selector_ids %}<option value="{{ analysis_id }}">{{ analysis_id }}</option>{% endfor %}
|
|
12
|
+
</select>
|
|
13
|
+
<label for="analysis-review-reason">이유</label><textarea id="analysis-review-reason" rows="3"></textarea>
|
|
14
|
+
<label for="analysis-review-evidence">추가 근거</label><textarea id="analysis-review-evidence" rows="3"></textarea>
|
|
15
|
+
<label for="analysis-review-scope-change">요청 범위 변경</label><textarea id="analysis-review-scope-change" rows="3"></textarea>
|
|
16
|
+
</section>
|
|
17
|
+
{%- endmacro %}
|
|
18
|
+
|
|
19
|
+
{% macro plan_approval(state) -%}
|
|
20
|
+
<section id="plan-approval" data-report-section="plan-approval">
|
|
21
|
+
<h2>계획 승인</h2>
|
|
22
|
+
<label for="approval-option">구현 옵션</label>
|
|
23
|
+
<select id="approval-option"{% if state.disabled_reason %} disabled{% endif %}>
|
|
24
|
+
{% for name in state.option_names %}
|
|
25
|
+
{% if name == state.recommended_option %}<option data-recommended="true" selected value="{{ name }}">{{ name }} (권장)</option>{% else %}<option value="{{ name }}">{{ name }}</option>{% endif %}
|
|
26
|
+
{% endfor %}
|
|
27
|
+
</select>
|
|
28
|
+
<input type="checkbox" id="approval-checkbox"{% if state.disabled_reason %} disabled{% endif %}><label for="approval-checkbox">이 계획을 승인합니다.</label>
|
|
29
|
+
{% if state.disabled_reason %}<div class="approval-disabled-reason"><p><strong>{{ state.disabled_reason }}</strong>라 아직 승인할 수 없습니다.</p><p>{{ state.blocker_ids | join(', ') }}에 답하고 사용자 응답을 내보낸 뒤 clarification 재개 명령으로 보고서를 다시 생성하세요.</p></div>{% endif %}
|
|
30
|
+
</section>
|
|
31
|
+
{%- endmacro %}
|
|
32
|
+
|
|
33
|
+
{% macro clarification_responses(items) -%}
|
|
34
|
+
{% if items %}
|
|
35
|
+
<section class="clarification-responses" aria-label="열린 질문에 답하기">
|
|
36
|
+
<h2>열린 질문에 답하기</h2>
|
|
37
|
+
<table><thead><tr><th>ID</th><th>질문</th><th>기대 형식</th><th>사용자 응답</th></tr></thead><tbody>
|
|
38
|
+
{% for row in items %}
|
|
39
|
+
<tr data-response-id="{{ row.id }}" data-kind="{{ row.kind }}">
|
|
40
|
+
<td>{{ row.id }}</td><td>{{ row.statement }}</td><td>{{ row.expectedForm }}</td>
|
|
41
|
+
<td><label for="response-{{ row.id }}">{{ row.id }} 사용자 응답</label><textarea id="response-{{ row.id }}" data-response-id="{{ row.id }}" rows="3">{{ row.userInput | default('') }}</textarea></td>
|
|
42
|
+
</tr>
|
|
43
|
+
{% endfor %}
|
|
44
|
+
</tbody></table>
|
|
45
|
+
</section>
|
|
46
|
+
{% endif %}
|
|
47
|
+
{%- endmacro %}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{% macro narrative(value, field_name) -%}
|
|
2
|
+
<div class="narrative" data-report-field="{{ field_name }}">
|
|
3
|
+
<p>{{ value.text }}</p>
|
|
4
|
+
<p class="evidence-refs">근거: {{ value.evidenceRefs | join(', ') }}</p>
|
|
5
|
+
</div>
|
|
6
|
+
{%- endmacro %}
|
|
7
|
+
|
|
8
|
+
{% macro action_list(actions) -%}
|
|
9
|
+
<ol class="action-list">
|
|
10
|
+
{% for action in actions %}<li>{{ action }}</li>{% endfor %}
|
|
11
|
+
</ol>
|
|
12
|
+
{%- endmacro %}
|
|
13
|
+
|
|
14
|
+
{% macro summary_card(title, body, tone='neutral') -%}
|
|
15
|
+
<article class="summary-card tone-{{ tone }}">
|
|
16
|
+
<h3>{{ title }}</h3>
|
|
17
|
+
<p>{{ body }}</p>
|
|
18
|
+
</article>
|
|
19
|
+
{%- endmacro %}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{% macro figure(model) -%}
|
|
2
|
+
<figure data-figure-kind="{{ model.kind }}" aria-labelledby="{{ model.figure_id }}-title" aria-describedby="{{ model.figure_id }}-summary">
|
|
3
|
+
<figcaption>
|
|
4
|
+
<strong id="{{ model.figure_id }}-title">{{ model.title }}</strong>
|
|
5
|
+
<span id="{{ model.figure_id }}-summary">{{ model.summary }}</span>
|
|
6
|
+
</figcaption>
|
|
7
|
+
<div class="visualization" aria-hidden="true">{{ model.svg | safe }}</div>
|
|
8
|
+
<table class="visualization-fallback">
|
|
9
|
+
<thead><tr><th>ID</th><th>이름</th><th>그룹</th><th>상태</th><th>설명</th></tr></thead>
|
|
10
|
+
<tbody>
|
|
11
|
+
{% for node in model.nodes %}
|
|
12
|
+
<tr data-fallback-id="{{ node.id }}"><td>{{ node.id }}</td><td>{{ node.label }}</td><td>{{ node.group }}</td><td>{{ node.status }}</td><td>{{ node.detail }}</td></tr>
|
|
13
|
+
{% endfor %}
|
|
14
|
+
</tbody>
|
|
15
|
+
</table>
|
|
16
|
+
</figure>
|
|
17
|
+
{%- endmacro %}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{% extends "html/base.template.html" %}
|
|
2
|
+
{% from "html/macros/layout.html" import narrative as render_narrative, summary_card %}
|
|
3
|
+
{% from "html/macros/visualizations.html" import figure %}
|
|
4
|
+
{% from "html/macros/forms.html" import analysis_review %}
|
|
5
|
+
|
|
6
|
+
{% block human_content %}
|
|
7
|
+
<section data-report-section="change-overview">
|
|
8
|
+
<h2>변경 개요</h2>
|
|
9
|
+
{{ render_narrative(narrative.changeExplanation, "changeImpactAnalysis.userNarrative.changeExplanation") }}
|
|
10
|
+
<p>{{ change.changeRequest.summary }}</p>
|
|
11
|
+
</section>
|
|
12
|
+
|
|
13
|
+
<section data-report-section="affected-and-unaffected">
|
|
14
|
+
<h2>영향받는 영역과 영향 없는 경계</h2>
|
|
15
|
+
{{ render_narrative(narrative.impactExplanation, "changeImpactAnalysis.userNarrative.impactExplanation") }}
|
|
16
|
+
<div data-report-field="changeImpactAnalysis.impactItems"><h3>영향받는 영역</h3>{% for row in change.impactItems %}{{ summary_card(row.id ~ " · " ~ row.target, row.impactKind ~ " / 영향 " ~ row.level ~ " / 신뢰 " ~ row.confidence) }}{% endfor %}</div>
|
|
17
|
+
<div data-report-field="changeImpactAnalysis.unaffectedBoundaries"><h3>영향 없는 경계</h3>{{ render_narrative(narrative.unaffectedExplanation, "changeImpactAnalysis.userNarrative.unaffectedExplanation") }}<ul>{% for row in change.unaffectedBoundaries %}<li>{{ row.summary }}</li>{% endfor %}</ul></div>
|
|
18
|
+
</section>
|
|
19
|
+
|
|
20
|
+
<section data-report-section="blast-radius" data-report-field="changeImpactAnalysis.dependencyBlastRadius">
|
|
21
|
+
<h2>파급 범위</h2>
|
|
22
|
+
{{ figure(blastRadiusFigure) }}
|
|
23
|
+
</section>
|
|
24
|
+
|
|
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 }}</li>{% endfor %}</ul></div>
|
|
28
|
+
<div data-report-field="changeImpactAnalysis.migrationImpact"><h3>마이그레이션</h3><ul>{% for row in change.migrationImpact %}<li>{{ row.summary }}</li>{% endfor %}</ul></div>
|
|
29
|
+
<div data-report-field="changeImpactAnalysis.rollbackImpact"><h3>롤백</h3><ul>{% for row in change.rollbackImpact %}<li>{{ row.summary }}</li>{% endfor %}</ul></div>
|
|
30
|
+
<div data-report-field="changeImpactAnalysis.securityAndPerformanceImpact"><h3>보안과 성능</h3><ul>{% for row in change.securityAndPerformanceImpact %}<li>{{ row.summary }}</li>{% endfor %}</ul></div>
|
|
31
|
+
</section>
|
|
32
|
+
|
|
33
|
+
<section data-report-section="planning-readiness" data-report-field="changeImpactAnalysis.planningInputs">
|
|
34
|
+
<h2>계획 준비도</h2>
|
|
35
|
+
{{ render_narrative(narrative.planningReadiness, "changeImpactAnalysis.userNarrative.planningReadiness") }}
|
|
36
|
+
<ul>{% for row in change.planningInputs %}<li>{{ row.constraint }}{% if row.unknown %} — 미확정{% endif %}</li>{% endfor %}</ul>
|
|
37
|
+
</section>
|
|
38
|
+
|
|
39
|
+
{{ analysis_review(analysisReviewIds) }}
|
|
40
|
+
{% endblock %}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{% extends "html/base.template.html" %}
|
|
2
|
+
{% from "html/macros/layout.html" import narrative as render_narrative, summary_card %}
|
|
3
|
+
{% from "html/macros/visualizations.html" import figure %}
|
|
4
|
+
|
|
5
|
+
{% block human_content %}
|
|
6
|
+
<section data-report-section="symptom">
|
|
7
|
+
<h2>사용자가 겪은 증상</h2>
|
|
8
|
+
{{ render_narrative(narrative.symptomExplanation, "errorAnalysis.userNarrative.symptomExplanation") }}
|
|
9
|
+
<blockquote data-report-field="errorAnalysis.symptomVerbatim">{{ error.symptomVerbatim }}</blockquote>
|
|
10
|
+
<p data-report-field="errorAnalysis.observableFailure">{{ error.observableFailure }}</p>
|
|
11
|
+
</section>
|
|
12
|
+
|
|
13
|
+
<section data-report-section="reproduction">
|
|
14
|
+
<h2>재현 상태</h2>
|
|
15
|
+
<p><span class="status status-{{ error.reproduction.status }}">{{ error.reproduction.status }}</span></p>
|
|
16
|
+
<ul>{% for item in error.reproduction.evidence %}<li>{{ item }}</li>{% endfor %}</ul>
|
|
17
|
+
{% if error.reproduction.blockedReason %}<p>{{ error.reproduction.blockedReason }}</p>{% endif %}
|
|
18
|
+
</section>
|
|
19
|
+
|
|
20
|
+
<section data-report-section="causes">
|
|
21
|
+
<h2>원인 후보</h2>
|
|
22
|
+
{{ render_narrative(narrative.causeExplanation, "errorAnalysis.userNarrative.causeExplanation") }}
|
|
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 }}</h3><p class="evidence-refs">지지 근거: {{ row.supportingEvidence | join(', ') }}</p></article>{% else %}<p>현재 확인된 원인 후보가 없습니다.</p>{% endfor %}</div>
|
|
25
|
+
</section>
|
|
26
|
+
|
|
27
|
+
<section data-report-section="uncertainty">
|
|
28
|
+
<h2>아직 확정할 수 없는 부분</h2>
|
|
29
|
+
{{ render_narrative(narrative.uncertaintyExplanation, "errorAnalysis.userNarrative.uncertaintyExplanation") }}
|
|
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(', ') }}</p><p data-report-field="errorAnalysis.causeCandidates.disproveWith"><strong>이 가설을 기각하는 방법:</strong> {{ row.disproveWith }}</p></article>{% endfor %}
|
|
31
|
+
</section>
|
|
32
|
+
|
|
33
|
+
<section data-report-section="next-diagnostic">
|
|
34
|
+
<h2>다음 진단 한 가지</h2>
|
|
35
|
+
{{ render_narrative(narrative.diagnosticGuidance, "errorAnalysis.userNarrative.diagnosticGuidance") }}
|
|
36
|
+
{{ summary_card("실행", error.nextDiagnostic.action, "important") }}
|
|
37
|
+
<p><strong>확인 신호:</strong> {{ error.nextDiagnostic.confirmingSignal }}</p>
|
|
38
|
+
<p><strong>기각 신호:</strong> {{ error.nextDiagnostic.rejectingSignal }}</p>
|
|
39
|
+
</section>
|
|
40
|
+
{% endblock %}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{% extends "html/base.template.html" %}
|
|
2
|
+
{% from "html/macros/layout.html" import narrative as render_narrative, summary_card %}
|
|
3
|
+
{% from "html/macros/visualizations.html" import figure %}
|
|
4
|
+
{% from "html/macros/forms.html" import analysis_review %}
|
|
5
|
+
|
|
6
|
+
{% block human_content %}
|
|
7
|
+
<section data-report-section="feature-overview">
|
|
8
|
+
<h2>기능 개요</h2>
|
|
9
|
+
{{ render_narrative(narrative.userBehaviorExplanation, "featureAnalysis.userNarrative.userBehaviorExplanation") }}
|
|
10
|
+
<p><strong>대상:</strong> {{ feature.target.requestedValue }}</p>
|
|
11
|
+
<ul>{% for row in feature.actors %}<li>{{ row.description }}</li>{% endfor %}</ul>
|
|
12
|
+
</section>
|
|
13
|
+
|
|
14
|
+
<section data-report-section="behavior-flow" data-report-field="featureAnalysis.flows">
|
|
15
|
+
<h2>사용자 행동 흐름</h2>
|
|
16
|
+
{{ render_narrative(narrative.flowExplanation, "featureAnalysis.userNarrative.flowExplanation") }}
|
|
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 }}</li>{% endfor %}</ol></article>{% endfor %}</div>
|
|
19
|
+
</section>
|
|
20
|
+
|
|
21
|
+
<section data-report-section="rules-and-state">
|
|
22
|
+
<h2>규칙과 상태 변경</h2>
|
|
23
|
+
{{ render_narrative(narrative.rulesAndStateExplanation, "featureAnalysis.userNarrative.rulesAndStateExplanation") }}
|
|
24
|
+
<div data-report-field="featureAnalysis.domainRules"><h3>도메인 규칙</h3>{% for row in feature.domainRules %}{{ summary_card(row.id ~ " · " ~ row.condition, 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>
|
|
26
|
+
</section>
|
|
27
|
+
|
|
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, row.input ~ " → " ~ row.output ~ ". 실패 시: " ~ row.failureHandling) }}{% endfor %}
|
|
31
|
+
</section>
|
|
32
|
+
|
|
33
|
+
<section data-report-section="test-gaps" data-report-field="featureAnalysis.testCoverage">
|
|
34
|
+
<h2>테스트와 공백</h2>
|
|
35
|
+
{{ render_narrative(narrative.testGapExplanation, "featureAnalysis.userNarrative.testGapExplanation") }}
|
|
36
|
+
{% for row in feature.testCoverage %}{{ summary_card(row.id, "테스트: " ~ (row.testPaths | join(', ')) ~ "; 공백: " ~ ((row.gaps | join(', ')) or '없음')) }}{% endfor %}
|
|
37
|
+
</section>
|
|
38
|
+
|
|
39
|
+
{{ analysis_review(analysisReviewIds) }}
|
|
40
|
+
{% endblock %}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{% extends "html/base.template.html" %}
|
|
2
|
+
{% from "html/macros/layout.html" import narrative as render_narrative, summary_card %}
|
|
3
|
+
{% from "html/macros/visualizations.html" import figure %}
|
|
4
|
+
|
|
5
|
+
{% block human_content %}
|
|
6
|
+
<section data-report-section="verification-verdict">
|
|
7
|
+
<h2>검증 판정</h2>
|
|
8
|
+
<p><span class="status status-{{ verdict.verdictToken }}">{{ verdict.verdictToken }}</span></p>
|
|
9
|
+
{{ render_narrative(narrative.verdictExplanation, "finalVerification.userNarrative.verdictExplanation") }}
|
|
10
|
+
</section>
|
|
11
|
+
|
|
12
|
+
<section data-report-section="requirement-results" data-report-field="finalVerification.validationEvidence">
|
|
13
|
+
<h2>요구사항별 결과</h2>
|
|
14
|
+
{{ render_narrative(narrative.coverageExplanation, "finalVerification.userNarrative.coverageExplanation") }}
|
|
15
|
+
{{ figure(coverageFigure) }}
|
|
16
|
+
</section>
|
|
17
|
+
|
|
18
|
+
<section data-report-section="blockers" data-report-field="finalVerification.acceptanceBlockers">
|
|
19
|
+
<h2>수락 차단 항목</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") }}{% else %}<p>수락을 막는 항목이 없습니다.</p>{% endfor %}</div>
|
|
22
|
+
</section>
|
|
23
|
+
|
|
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 }}</p><p><strong>담당:</strong> {{ row.owner }}</p><p><strong>상향 조건:</strong> {{ row.escalationTrigger }}</p></article>{% else %}<p>기록된 잔여 위험이 없습니다.</p>{% endfor %}</div>
|
|
27
|
+
</section>
|
|
28
|
+
|
|
29
|
+
<section data-report-section="manual-results" data-report-field="finalVerification.manualUserTest">
|
|
30
|
+
<h2>수동 사용자 테스트 결과</h2>
|
|
31
|
+
{% if final.manualUserTest.applicable %}{% for row in final.manualUserTest.results %}<article class="summary-card tone-{{ row.result }}"><h3>{{ row.target }} · {{ row.result }}</h3><p>{{ row.performed }}</p><p>{{ row.observed }}</p></article>{% endfor %}{% else %}<p>{{ final.manualUserTest.exemptionReaffirm }}</p>{% endif %}
|
|
32
|
+
</section>
|
|
33
|
+
|
|
34
|
+
<section data-report-section="release-route">
|
|
35
|
+
<h2>릴리스 경로</h2>
|
|
36
|
+
{{ render_narrative(narrative.releaseReadiness, "finalVerification.userNarrative.releaseReadiness") }}
|
|
37
|
+
{% if releaseAllowed %}<p>{{ final.routingRecommendation }}</p>{% else %}<p>현재 판정으로는 release-handoff를 시작할 수 없습니다. 차단 또는 조건을 먼저 해소하세요.</p>{% endif %}
|
|
38
|
+
</section>
|
|
39
|
+
{% endblock %}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{% extends "html/base.template.html" %}
|
|
2
|
+
{% from "html/macros/forms.html" import plan_approval %}
|
|
3
|
+
{% from "html/macros/layout.html" import narrative as render_narrative, action_list, summary_card %}
|
|
4
|
+
{% from "html/macros/visualizations.html" import figure %}
|
|
5
|
+
|
|
6
|
+
{% block human_content %}
|
|
7
|
+
<section data-report-section="planning-goal">
|
|
8
|
+
<h2>계획 목표</h2>
|
|
9
|
+
<p>{{ humanSummary.outcome }}</p>
|
|
10
|
+
<div class="summary-grid">{% for reason in humanSummary.whyItMatters %}{{ summary_card("왜 중요한가", reason, "important") }}{% endfor %}</div>
|
|
11
|
+
</section>
|
|
12
|
+
|
|
13
|
+
<section data-report-section="option-comparison">
|
|
14
|
+
<h2>구현 대안 비교</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 }}</h3><p><strong>인터페이스:</strong> {{ row.interfaces }}</p><p><strong>영향 범위:</strong> {{ row.blastRadius }}</p><ul>{% for file in row.fileStructure %}<li>{{ file.action }} <code>{{ file.path }}</code> — {{ file.summary }}</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 }}</td><td>{{ row.complexity }}</td><td>{{ row.risk }}</td><td>{{ row.reversibility }}</td><td>{{ row.testCoverageCost }}</td><td>{{ row.rolloutCost }}</td></tr>{% endfor %}</tbody></table>
|
|
18
|
+
</section>
|
|
19
|
+
|
|
20
|
+
<section data-report-section="recommendation" data-report-field="implementationPlanning.recommendedOption">
|
|
21
|
+
<h2>권장안</h2>
|
|
22
|
+
{{ render_narrative(narrative.recommendationExplanation, "implementationPlanning.userNarrative.recommendationExplanation") }}
|
|
23
|
+
<article class="summary-card tone-important"><h3>{{ planning.recommendedOption.name }}</h3><p>{{ planning.recommendedOption.coreReason }}</p><p>{{ planning.recommendedOption.rationale }}</p><p><strong>선택하지 않은 안:</strong> {{ planning.recommendedOption.rejectedSummary }}</p></article>
|
|
24
|
+
</section>
|
|
25
|
+
|
|
26
|
+
<section data-report-section="stage-map" data-report-field="implementationPlanning.stageMap">
|
|
27
|
+
<h2>단계별 실행 지도</h2>
|
|
28
|
+
{{ render_narrative(narrative.stageStrategy, "implementationPlanning.userNarrative.stageStrategy") }}
|
|
29
|
+
{{ figure(stageFigure) }}
|
|
30
|
+
<div class="summary-grid">{% for row in planning.stages %}<article class="summary-card"><h3>Stage {{ row.stage }} · {{ row.title }}</h3><p>{{ row.sliceValue }}</p><p><strong>완료 조건:</strong> {{ row.acceptance }}</p><p><strong>검증:</strong> {{ row.stageValidation }}</p></article>{% endfor %}</div>
|
|
31
|
+
</section>
|
|
32
|
+
|
|
33
|
+
<section data-report-section="validation-and-rollback">
|
|
34
|
+
<h2>검증과 롤백</h2>
|
|
35
|
+
{{ render_narrative(narrative.validationAndRollback, "implementationPlanning.userNarrative.validationAndRollback") }}
|
|
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 }}</td><td><code>{{ row.commandOrObservation }}</code></td><td>{{ row.expectedOutcome }}</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, row.action, "important") }}{% endfor %}</div>
|
|
38
|
+
</section>
|
|
39
|
+
|
|
40
|
+
<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>
|
|
43
|
+
{{ action_list(humanSummary.actions) }}
|
|
44
|
+
</section>
|
|
45
|
+
|
|
46
|
+
{{ plan_approval(approval) }}
|
|
47
|
+
{% endblock %}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{% extends "html/base.template.html" %}
|
|
2
|
+
{% from "html/macros/layout.html" import narrative as render_narrative, summary_card %}
|
|
3
|
+
{% from "html/macros/visualizations.html" import figure %}
|
|
4
|
+
|
|
5
|
+
{% block human_content %}
|
|
6
|
+
<section data-report-section="delivered-outcome">
|
|
7
|
+
<h2>전달된 결과</h2>
|
|
8
|
+
{{ render_narrative(narrative.deliveredOutcome, "implementation.userNarrative.deliveredOutcome") }}
|
|
9
|
+
<p>{{ humanSummary.outcome }}</p>
|
|
10
|
+
</section>
|
|
11
|
+
|
|
12
|
+
<section data-report-section="change-map" data-report-field="implementation.diffSummary">
|
|
13
|
+
<h2>무엇이 달라졌는가</h2>
|
|
14
|
+
{{ render_narrative(narrative.changeExplanation, "implementation.userNarrative.changeExplanation") }}
|
|
15
|
+
{{ figure(changeFigure) }}
|
|
16
|
+
</section>
|
|
17
|
+
|
|
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 }}</td><td>{{ row.coveredBy }}</td><td><span class="status status-{{ row.status }}">{{ row.status }}</span></td></tr>{% endfor %}</tbody></table>
|
|
21
|
+
</section>
|
|
22
|
+
|
|
23
|
+
<section data-report-section="validation" data-report-field="implementation.validationEvidence">
|
|
24
|
+
<h2>검증 결과</h2>
|
|
25
|
+
{{ render_narrative(narrative.validationExplanation, "implementation.userNarrative.validationExplanation") }}
|
|
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>
|
|
28
|
+
</section>
|
|
29
|
+
|
|
30
|
+
<section data-report-section="remaining-issues">
|
|
31
|
+
<h2>남은 문제</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") }}{% else %}<p>계획 밖 수정 사항이 없습니다.</p>{% endfor %}</div>
|
|
34
|
+
</section>
|
|
35
|
+
|
|
36
|
+
<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 }}</h3><p><strong>환경:</strong> {{ row.environmentSetup }}</p><ol>{% for step in row.steps %}<li>{{ step }}</li>{% endfor %}</ol><p><strong>기대 결과:</strong> {{ row.expectedResult }}</p></article>{% endfor %}{% else %}<p>{{ implementation.manualUserTest.exemptionReason }}</p>{% endif %}
|
|
39
|
+
</section>
|
|
40
|
+
{% endblock %}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{% extends "html/base.template.html" %}
|
|
2
|
+
{% from "html/macros/layout.html" import narrative as render_narrative, summary_card %}
|
|
3
|
+
{% from "html/macros/visualizations.html" import figure %}
|
|
4
|
+
|
|
5
|
+
{% block human_content %}
|
|
6
|
+
<section data-report-section="opportunities">
|
|
7
|
+
<h2>발견한 개선 기회</h2>
|
|
8
|
+
{{ render_narrative(narrative.opportunityOverview, "improvementDiscovery.userNarrative.opportunityOverview") }}
|
|
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 }}</td><td>{{ row.evidence | join(', ') }}</td></tr>{% endfor %}</tbody></table>
|
|
10
|
+
</section>
|
|
11
|
+
|
|
12
|
+
<section data-report-section="ranked-candidates">
|
|
13
|
+
<h2>우선순위 후보</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 }}"><p class="eyebrow">{{ loop.index }}순위 · {{ row.id }} · 영향 {{ row.severity }} · 노력 {{ row.effort }}</p><h3>{{ row.title }}</h3><p>{{ row.expectedBehaviorAfter }}</p><p><strong>다음 단계:</strong> {{ row.recommendedNextPhase }}</p><p class="evidence-refs">근거: {{ row.evidence | join(', ') }}</p></article>{% else %}<p>선택할 개선 후보가 없습니다.</p>{% endfor %}</div>
|
|
16
|
+
</section>
|
|
17
|
+
|
|
18
|
+
<section data-report-section="impact-effort">
|
|
19
|
+
<h2>영향도와 노력도</h2>
|
|
20
|
+
{{ figure(matrixFigure) }}
|
|
21
|
+
</section>
|
|
22
|
+
|
|
23
|
+
<section data-report-section="selection">
|
|
24
|
+
<h2>선택 안내</h2>
|
|
25
|
+
{{ render_narrative(narrative.selectionGuidance, "improvementDiscovery.userNarrative.selectionGuidance") }}
|
|
26
|
+
<p><strong>선택 상한:</strong> 최대 {{ improvement.selectionLimit }}개</p>
|
|
27
|
+
<ul>{% for row in improvement.candidates %}<li><label><input type="checkbox" name="improvement-candidate" value="{{ row.id }}"> {{ row.id }} · {{ row.title }}</label></li>{% endfor %}</ul>
|
|
28
|
+
</section>
|
|
29
|
+
{% endblock %}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{% extends "html/base.template.html" %}
|
|
2
|
+
{% from "html/macros/layout.html" import narrative as render_narrative, action_list, summary_card %}
|
|
3
|
+
{% from "html/macros/visualizations.html" import figure %}
|
|
4
|
+
|
|
5
|
+
{% block human_content %}
|
|
6
|
+
<section data-report-section="project-overview">
|
|
7
|
+
<h2>프로젝트 개요</h2>
|
|
8
|
+
{{ render_narrative(narrative.projectOverview, "projectAnalysis.userNarrative.projectOverview") }}
|
|
9
|
+
<div class="summary-grid">
|
|
10
|
+
{% for reason in humanSummary.whyItMatters %}{{ summary_card("왜 중요한가", reason, "important") }}{% endfor %}
|
|
11
|
+
</div>
|
|
12
|
+
</section>
|
|
13
|
+
|
|
14
|
+
<section data-report-section="architecture" data-report-field="projectAnalysis.components">
|
|
15
|
+
<h2>아키텍처와 의존성</h2>
|
|
16
|
+
{{ render_narrative(narrative.architectureExplanation, "projectAnalysis.userNarrative.architectureExplanation") }}
|
|
17
|
+
{{ figure(architectureFigure) }}
|
|
18
|
+
</section>
|
|
19
|
+
|
|
20
|
+
<section data-report-section="boundaries">
|
|
21
|
+
<h2>시스템 경계</h2>
|
|
22
|
+
<div class="summary-grid" data-report-field="projectAnalysis.entryPoints">
|
|
23
|
+
{% for row in project.entryPoints %}{{ summary_card(row.symbol, row.role) }}{% endfor %}
|
|
24
|
+
</div>
|
|
25
|
+
<h3>데이터 저장소</h3>
|
|
26
|
+
<ul data-report-field="projectAnalysis.dataStores">{% for row in project.dataStores %}<li><strong>{{ row.name }}</strong> — {{ row.readBoundary }} / {{ row.writeBoundary }}</li>{% endfor %}</ul>
|
|
27
|
+
<h3>외부 시스템</h3>
|
|
28
|
+
<ul data-report-field="projectAnalysis.externalSystems">{% for row in project.externalSystems %}<li><strong>{{ row.name }}</strong> — {{ row.adapter }} ({{ row.direction }})</li>{% endfor %}</ul>
|
|
29
|
+
</section>
|
|
30
|
+
|
|
31
|
+
<section data-report-section="hotspots" data-report-field="projectAnalysis.rankedHotspots">
|
|
32
|
+
<h2>우선 검토할 핫스팟</h2>
|
|
33
|
+
{{ render_narrative(narrative.hotspotExplanation, "projectAnalysis.userNarrative.hotspotExplanation") }}
|
|
34
|
+
<div class="summary-grid">
|
|
35
|
+
{% for row in project.rankedHotspots %}
|
|
36
|
+
<article class="summary-card tone-{{ row.risk }}"><p class="eyebrow">{{ row.id }} · 영향 {{ row.impact }} · 위험 {{ row.risk }}</p><h3>{{ row.title }}</h3><p>{{ row.reason }}</p><p class="evidence-refs">근거: {{ row.evidenceRefs | join(', ') }}</p></article>
|
|
37
|
+
{% endfor %}
|
|
38
|
+
</div>
|
|
39
|
+
</section>
|
|
40
|
+
|
|
41
|
+
<section data-report-section="quality-and-tests" data-report-field="projectAnalysis.qualityCoverage">
|
|
42
|
+
<h2>품질과 테스트 상태</h2>
|
|
43
|
+
{{ render_narrative(narrative.qualityExplanation, "projectAnalysis.userNarrative.qualityExplanation") }}
|
|
44
|
+
<table><thead><tr><th>영역</th><th>상태</th><th>설명</th><th>근거</th></tr></thead><tbody>{% for row in project.qualityCoverage %}<tr><td>{{ row.area }}</td><td><span class="status status-{{ row.status }}">{{ row.status }}</span></td><td>{{ row.summary }}</td><td>{{ row.evidenceRefs | join(', ') }}</td></tr>{% endfor %}</tbody></table>
|
|
45
|
+
</section>
|
|
46
|
+
|
|
47
|
+
<section data-report-section="feature-map" data-report-field="projectAnalysis.featureIndex">
|
|
48
|
+
<h2>기능 지도</h2>
|
|
49
|
+
<div class="summary-grid">{% for row in project.featureIndex %}{{ summary_card(row.id ~ " · " ~ row.name, row.description) }}{% endfor %}</div>
|
|
50
|
+
</section>
|
|
51
|
+
|
|
52
|
+
<section data-report-section="user-actions">
|
|
53
|
+
<h2>다음 행동</h2>
|
|
54
|
+
{{ action_list(humanSummary.actions) }}
|
|
55
|
+
{% if humanSummary.blockers %}<h3>차단 사항</h3><ul>{% for item in humanSummary.blockers %}<li>{{ item }}</li>{% endfor %}</ul>{% endif %}
|
|
56
|
+
</section>
|
|
57
|
+
{% endblock %}
|