okstra 0.186.6 → 0.186.7
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/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/python/okstra_ctl/plan_items_cli.py +1 -0
- package/runtime/python/okstra_ctl/report_contract.py +2 -0
- package/runtime/python/okstra_ctl/report_html/common.py +2 -7
- package/runtime/python/okstra_ctl/report_html/render.py +3 -0
- package/runtime/python/okstra_ctl/report_html/view_models/implementation_planning.py +2 -5
- package/runtime/schemas/final-report-v2.0.schema.json +4 -0
- package/runtime/schemas/final-report-v3.0.schema.json +4 -0
- package/runtime/templates/reports/html/base.template.html +13 -0
- package/runtime/templates/reports/html/i18n/en.json +19 -0
- package/runtime/templates/reports/html/i18n/ko.json +19 -0
- package/runtime/templates/reports/html/tasks/final-verification.template.html +2 -2
- package/runtime/templates/reports/html/tasks/implementation-planning.template.html +19 -9
- package/runtime/templates/reports/html/tasks/implementation.template.html +1 -1
package/package.json
CHANGED
package/runtime/BUILD.json
CHANGED
|
@@ -641,6 +641,7 @@ def _verdict_row(worker: str, block: VerdictBlock) -> dict[str, Any]:
|
|
|
641
641
|
("breakageKind", block.breakage_kind),
|
|
642
642
|
("fixability", block.fixability),
|
|
643
643
|
("note", block.note),
|
|
644
|
+
("explanation", block.explanation),
|
|
644
645
|
):
|
|
645
646
|
if value:
|
|
646
647
|
row[key] = value
|
|
@@ -130,6 +130,7 @@ IMPLEMENTATION_PLANNING_LEGACY_REQUIRED_HUMAN_FIELDS = (
|
|
|
130
130
|
"implementationPlanning.tradeoffMatrix",
|
|
131
131
|
"implementationPlanning.recommendedOption",
|
|
132
132
|
"implementationPlanning.stageMap",
|
|
133
|
+
"implementationPlanning.planBodyVerification",
|
|
133
134
|
)
|
|
134
135
|
|
|
135
136
|
IMPLEMENTATION_PLANNING_PLAN_READY_REQUIRED_HUMAN_FIELDS = (
|
|
@@ -138,6 +139,7 @@ IMPLEMENTATION_PLANNING_PLAN_READY_REQUIRED_HUMAN_FIELDS = (
|
|
|
138
139
|
"implementationPlanning.coverageSummary",
|
|
139
140
|
"implementationPlanning.stageMap",
|
|
140
141
|
"implementationPlanning.requirementCoverage",
|
|
142
|
+
"implementationPlanning.planBodyVerification",
|
|
141
143
|
)
|
|
142
144
|
|
|
143
145
|
IMPLEMENTATION_PLANNING_INVALIDATED_REQUIRED_HUMAN_FIELDS = (
|
|
@@ -2,11 +2,8 @@
|
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
# key hunt across all of them would misread `crossVerification.consensus`, whose
|
|
8
|
-
# `evidence` key holds provenance while `evidence.primary` uses the same word
|
|
9
|
-
# for content — so the block a row came from is named here rather than guessed.
|
|
5
|
+
# 본문이 인용하지만 전용 섹션이 없는 행만 대장에 넣는다. 키 이름만으로
|
|
6
|
+
# 블록을 고르면 `evidence.primary` 의 내용과 다른 블록의 출처 칸이 섞인다.
|
|
10
7
|
#
|
|
11
8
|
# Source and confidence are separate columns because most blocks carry only one
|
|
12
9
|
# of the two: `evidence.primary` cites a file and never rates itself, while
|
|
@@ -29,8 +26,6 @@ _LEDGER_BLOCKS = (
|
|
|
29
26
|
(("analysisCommon", "confirmedFacts"), "statement", "", "", "confirmed-fact"),
|
|
30
27
|
(("analysisCommon", "inferences"), "statement", "", "confidence", "inference"),
|
|
31
28
|
(("analysisCommon", "unknowns"), "question", "reason", "", "unknown"),
|
|
32
|
-
(("crossVerification", "consensus"), "statement", "evidence", "", "cross-check-consensus"),
|
|
33
|
-
(("crossVerification", "differences"), "disagreement", "workersPosition", "", "cross-check-dissent"),
|
|
34
29
|
(("missingInformation",), "item", "risk", "", "missing-information"),
|
|
35
30
|
(("followUpTasks",), "title", "reason", "", "follow-up"),
|
|
36
31
|
)
|
|
@@ -157,6 +157,9 @@ def render_v2_html_view(
|
|
|
157
157
|
"sourceData": source_data,
|
|
158
158
|
"dataSha256": _sha256(data_path),
|
|
159
159
|
"clarificationItems": data.get("clarificationItems", []),
|
|
160
|
+
# 합의·이견 근거는 태스크 본문과 같이 기본 화면에 올린다. 근거 대장
|
|
161
|
+
# 감사 모드에만 두면 판정이 사용자에게 안 보인다.
|
|
162
|
+
"crossVerification": data.get("crossVerification") or {},
|
|
160
163
|
# Every task type ends with the same run-cost section, so it is bound
|
|
161
164
|
# here rather than in ten view models that would each rebuild it.
|
|
162
165
|
"runUsage": run_usage(data),
|
|
@@ -89,16 +89,13 @@ def _stage_figure(planning: dict):
|
|
|
89
89
|
return stage_map_figure(nodes=nodes, edges=edges, title="Implementation stage dependencies")
|
|
90
90
|
|
|
91
91
|
|
|
92
|
-
#
|
|
93
|
-
#
|
|
94
|
-
# needs, and the verification rounds the audit trail keeps. Declared here so the
|
|
95
|
-
# ids they carry stop being anchor targets in this document.
|
|
92
|
+
# 승인자가 보지 않는 구현자·사고 대응 표. 계획 본문 검증은 판정과 근거가
|
|
93
|
+
# 승인 판단이라 여기 두지 않는다.
|
|
96
94
|
_OMITTED_FIELDS = (
|
|
97
95
|
"validationChecklist",
|
|
98
96
|
"crossProjectDependencies",
|
|
99
97
|
"dependencyMigrationRisk",
|
|
100
98
|
"rollbackStrategy",
|
|
101
|
-
"planBodyVerification",
|
|
102
99
|
)
|
|
103
100
|
|
|
104
101
|
|
|
@@ -9330,6 +9330,10 @@
|
|
|
9330
9330
|
"note": {
|
|
9331
9331
|
"type": "string"
|
|
9332
9332
|
},
|
|
9333
|
+
"explanation": {
|
|
9334
|
+
"type": "string",
|
|
9335
|
+
"description": "The worker's two-to-three-sentence verdict rationale. Distinct from `note`, which records the falsification candidate considered even on AGREE."
|
|
9336
|
+
},
|
|
9333
9337
|
"round": {
|
|
9334
9338
|
"description": "The plan-body verification round this verdict was cast in. A self-fix round rewrites the plan after a verification round, so a verdict whose round is at or before `selfFixRoundsApplied` judged text that has since changed. Without it there is no way to tell how many rewrites a surviving verdict predates, and a gate can pass on judgements two generations stale.",
|
|
9335
9339
|
"type": "integer",
|
|
@@ -9322,6 +9322,10 @@
|
|
|
9322
9322
|
"note": {
|
|
9323
9323
|
"type": "string"
|
|
9324
9324
|
},
|
|
9325
|
+
"explanation": {
|
|
9326
|
+
"type": "string",
|
|
9327
|
+
"description": "The worker's two-to-three-sentence verdict rationale. Distinct from `note`, which records the falsification candidate considered even on AGREE."
|
|
9328
|
+
},
|
|
9325
9329
|
"round": {
|
|
9326
9330
|
"description": "The plan-body verification round this verdict was cast in. A self-fix round rewrites the plan after a verification round, so a verdict whose round is at or before `selfFixRoundsApplied` judged text that has since changed. Without it there is no way to tell how many rewrites a surviving verdict predates, and a gate can pass on judgements two generations stale.",
|
|
9327
9331
|
"type": "integer",
|
|
@@ -44,6 +44,19 @@
|
|
|
44
44
|
</header>
|
|
45
45
|
<main id="main-content" data-report-role="human-main">
|
|
46
46
|
{% block human_content %}{% endblock %}
|
|
47
|
+
{% if crossVerification.get("consensus") or crossVerification.get("differences") %}
|
|
48
|
+
<section data-report-section="cross-check">
|
|
49
|
+
<h2>{{ t('base.cross-check') }}</h2>
|
|
50
|
+
{% if crossVerification.get("consensus") %}
|
|
51
|
+
<h3>{{ t('base.agreed-across-workers') }}</h3>
|
|
52
|
+
<div class="summary-grid">{% for row in crossVerification.consensus %}<article class="summary-card" id="id-xv-{{ row.id }}"><p class="eyebrow">{{ row.id | inline_code }}</p><h3>{{ row.statement | inline_code }}</h3><p class="evidence-refs">{{ t('macros.layout.evidence') }} {{ row.evidence | inline_code }}</p></article>{% endfor %}</div>
|
|
53
|
+
{% endif %}
|
|
54
|
+
{% if crossVerification.get("differences") %}
|
|
55
|
+
<h3>{{ t('base.workers-disagreed') }}</h3>
|
|
56
|
+
<div class="summary-grid">{% for row in crossVerification.differences %}<article class="summary-card tone-important" id="id-xv-{{ row.id }}"><p class="eyebrow">{{ row.id | inline_code }}</p><h3>{{ row.disagreement | inline_code }}</h3><p>{% for pos in row.get("workersPosition", []) %}{{ pos.worker | inline_code }} · {{ pos.position | inline_code }}{% if not loop.last %} · {% endif %}{% endfor %}</p><p class="evidence-refs">{{ t('macros.layout.evidence') }} {{ row.evidence | inline_code }}</p></article>{% endfor %}</div>
|
|
57
|
+
{% endif %}
|
|
58
|
+
</section>
|
|
59
|
+
{% endif %}
|
|
47
60
|
{{ clarification_responses(clarificationItems) }}
|
|
48
61
|
{% if evidenceIndex %}
|
|
49
62
|
<section data-report-section="evidence-ledger" data-reader-kind="audit">
|
|
@@ -72,6 +72,12 @@
|
|
|
72
72
|
"full": "Replanned in full",
|
|
73
73
|
"incremental": "Partly replanned"
|
|
74
74
|
},
|
|
75
|
+
"planGate": {
|
|
76
|
+
"passed": "Passed",
|
|
77
|
+
"passed-with-dissent": "Passed with dissent",
|
|
78
|
+
"blocked-by-disagreement": "Blocked by disagreement",
|
|
79
|
+
"aborted-non-result": "Verification produced no result"
|
|
80
|
+
},
|
|
75
81
|
"ledgerKind": {
|
|
76
82
|
"code-evidence": "Code evidence",
|
|
77
83
|
"hypothesis": "Hypothesis",
|
|
@@ -106,6 +112,9 @@
|
|
|
106
112
|
"written": "Written",
|
|
107
113
|
"elapsed": "Elapsed",
|
|
108
114
|
"evidence-ledger": "Evidence ledger",
|
|
115
|
+
"cross-check": "Worker agreement and dissent",
|
|
116
|
+
"agreed-across-workers": "Agreed across workers",
|
|
117
|
+
"workers-disagreed": "Workers disagreed",
|
|
109
118
|
"source": "Source",
|
|
110
119
|
"confidence": "Confidence",
|
|
111
120
|
"export-my-answers": "Export my answers",
|
|
@@ -293,6 +302,7 @@
|
|
|
293
302
|
"the-change-added-no-new-surface": "The change added no new identifier, module, or configuration entry.",
|
|
294
303
|
"what-blocks-acceptance": "What blocks acceptance",
|
|
295
304
|
"nothing-is-holding-acceptance": "Nothing is holding acceptance.",
|
|
305
|
+
"follow-up": "Follow-up:",
|
|
296
306
|
"residual-risk": "Residual risk",
|
|
297
307
|
"owner": "Owner:",
|
|
298
308
|
"escalation-condition": "Escalation condition:",
|
|
@@ -382,6 +392,12 @@
|
|
|
382
392
|
"mitigation": "Mitigation",
|
|
383
393
|
"rollback-strategy": "Rollback strategy",
|
|
384
394
|
"plan-body-verification": "Plan body verification",
|
|
395
|
+
"source-section": "source §",
|
|
396
|
+
"breakage": "Breakage",
|
|
397
|
+
"fixability": "Fixability",
|
|
398
|
+
"claim": "Claim",
|
|
399
|
+
"reproduction": "Reproduction",
|
|
400
|
+
"note": "Note:",
|
|
385
401
|
"verdict": "Verdict",
|
|
386
402
|
"what-it-blocked": "What it blocked",
|
|
387
403
|
"plan-item": "Plan item",
|
|
@@ -439,6 +455,9 @@
|
|
|
439
455
|
"implemented-by": "Implemented by",
|
|
440
456
|
"verification-result": "Verification result",
|
|
441
457
|
"independent-check": "Independent check:",
|
|
458
|
+
"independent-rerun": "Independent re-run:",
|
|
459
|
+
"command-log": "Command log:",
|
|
460
|
+
"discrepancy": "Discrepancy:",
|
|
442
461
|
"exit-code": "exit code",
|
|
443
462
|
"what-is-left": "What is left",
|
|
444
463
|
"nothing-was-changed-outside-the-plan": "Nothing was changed outside the plan.",
|
|
@@ -72,6 +72,12 @@
|
|
|
72
72
|
"full": "전부 다시 계획함",
|
|
73
73
|
"incremental": "일부만 다시 계획함"
|
|
74
74
|
},
|
|
75
|
+
"planGate": {
|
|
76
|
+
"passed": "통과",
|
|
77
|
+
"passed-with-dissent": "이견이 남았지만 통과",
|
|
78
|
+
"blocked-by-disagreement": "이견으로 차단",
|
|
79
|
+
"aborted-non-result": "검증이 결과를 내지 못함"
|
|
80
|
+
},
|
|
75
81
|
"ledgerKind": {
|
|
76
82
|
"code-evidence": "코드 근거",
|
|
77
83
|
"hypothesis": "가설",
|
|
@@ -106,6 +112,9 @@
|
|
|
106
112
|
"written": "작성",
|
|
107
113
|
"elapsed": "소요 시간",
|
|
108
114
|
"evidence-ledger": "근거 대장",
|
|
115
|
+
"cross-check": "작업자 합의와 이견",
|
|
116
|
+
"agreed-across-workers": "작업자 간 합의",
|
|
117
|
+
"workers-disagreed": "작업자 간 이견",
|
|
109
118
|
"source": "출처",
|
|
110
119
|
"confidence": "확신도",
|
|
111
120
|
"export-my-answers": "내 답변 내보내기",
|
|
@@ -293,6 +302,7 @@
|
|
|
293
302
|
"the-change-added-no-new-surface": "이 변경이 새로 추가한 식별자·모듈·설정 항목은 없습니다.",
|
|
294
303
|
"what-blocks-acceptance": "수용을 막는 것",
|
|
295
304
|
"nothing-is-holding-acceptance": "수용을 막는 것이 없습니다.",
|
|
305
|
+
"follow-up": "후속:",
|
|
296
306
|
"residual-risk": "남은 위험",
|
|
297
307
|
"owner": "담당:",
|
|
298
308
|
"escalation-condition": "에스컬레이션 조건:",
|
|
@@ -382,6 +392,12 @@
|
|
|
382
392
|
"mitigation": "완화 방안",
|
|
383
393
|
"rollback-strategy": "롤백 전략",
|
|
384
394
|
"plan-body-verification": "계획 본문 검증",
|
|
395
|
+
"source-section": "출처 §",
|
|
396
|
+
"breakage": "결함 종류",
|
|
397
|
+
"fixability": "수정 가능성",
|
|
398
|
+
"claim": "주장",
|
|
399
|
+
"reproduction": "재현",
|
|
400
|
+
"note": "메모:",
|
|
385
401
|
"verdict": "판정",
|
|
386
402
|
"what-it-blocked": "무엇을 막았는지",
|
|
387
403
|
"plan-item": "계획 항목",
|
|
@@ -439,6 +455,9 @@
|
|
|
439
455
|
"implemented-by": "구현한 곳",
|
|
440
456
|
"verification-result": "검증 결과",
|
|
441
457
|
"independent-check": "독립 검증:",
|
|
458
|
+
"independent-rerun": "독립 재실행:",
|
|
459
|
+
"command-log": "명령 기록:",
|
|
460
|
+
"discrepancy": "불일치:",
|
|
442
461
|
"exit-code": "종료 코드",
|
|
443
462
|
"what-is-left": "남은 것",
|
|
444
463
|
"nothing-was-changed-outside-the-plan": "계획 밖에서 바뀐 것이 없습니다.",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{% extends "html/base.template.html" %}
|
|
2
|
-
{% from "html/macros/layout.html" import narrative as render_narrative
|
|
2
|
+
{% from "html/macros/layout.html" import narrative as render_narrative %}
|
|
3
3
|
{% from "html/macros/visualizations.html" import figure %}
|
|
4
4
|
|
|
5
5
|
{% block human_content %}
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
<section data-report-section="blockers" data-report-field="finalVerification.acceptanceBlockers">
|
|
25
25
|
<h2>{{ t('tasks.final-verification.what-blocks-acceptance') }}</h2>
|
|
26
26
|
{{ render_narrative(narrative.blockerExplanation, "finalVerification.userNarrative.blockerExplanation") }}
|
|
27
|
-
<div class="summary-grid">{% for row in final.acceptanceBlockers %}{{
|
|
27
|
+
<div class="summary-grid">{% for row in final.acceptanceBlockers %}<article class="summary-card tone-important" id="id-{{ row.id }}"><p class="eyebrow">{{ row.id | inline_code }} · {{ row.severity }}</p><h3>{{ row.statement | inline_code }}</h3><p class="evidence-refs">{{ t('macros.layout.evidence') }} {{ row.evidence | inline_code }}</p><p>{{ t('tasks.final-verification.follow-up') }} {{ row.followUpPhase | inline_code }}</p></article>{% else %}<p>{{ t('tasks.final-verification.nothing-is-holding-acceptance') }}</p>{% endfor %}</div>
|
|
28
28
|
</section>
|
|
29
29
|
|
|
30
30
|
<section data-report-section="residual-risk" data-report-field="finalVerification.residualRisk">
|
|
@@ -84,15 +84,9 @@
|
|
|
84
84
|
</section>
|
|
85
85
|
{% endif %}
|
|
86
86
|
|
|
87
|
-
{#
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
approver's: this reader is deciding whether to greenlight the plan, and none
|
|
91
|
-
of those tables move that decision. They stay in the markdown report, which
|
|
92
|
-
is what the next phase and the audit trail read. Requirement coverage is the
|
|
93
|
-
exception — "does this plan actually do what the brief asked" is the
|
|
94
|
-
approval question itself. A-NNN citations still need an id in this document
|
|
95
|
-
or the checkRef links land nowhere. #}
|
|
87
|
+
{# 검증 체크리스트·프로젝트 간 의존·마이그레이션 위험·롤백·활동 대장은
|
|
88
|
+
구현자와 감사 추적용이다. 계획 본문 검증의 판정과 근거는 승인 판단이라
|
|
89
|
+
이 화면에 남긴다. A-NNN 인용은 이 문서에 id 가 있어야 checkRef 가 산다. #}
|
|
96
90
|
<section data-report-section="requirement-coverage" data-report-field="implementationPlanning.requirementCoverage">
|
|
97
91
|
<h2>{{ t('tasks.implementation-planning.how-each-requirement-gets-met') }}</h2>
|
|
98
92
|
{% if planning.get("planningContract") == "selected-direction" %}
|
|
@@ -102,6 +96,22 @@
|
|
|
102
96
|
{% endif %}
|
|
103
97
|
</section>
|
|
104
98
|
|
|
99
|
+
{% if planning.get("planBodyVerification") %}
|
|
100
|
+
<section data-report-section="plan-body-verification" data-report-field="implementationPlanning.planBodyVerification">
|
|
101
|
+
<h2>{{ t('tasks.implementation-planning.plan-body-verification') }}</h2>
|
|
102
|
+
<p><span class="status status-{{ planning.planBodyVerification.gateResult }}">{{ planning.planBodyVerification.gateResult | enum_label('planGate') }}</span></p>
|
|
103
|
+
{% if planning.planBodyVerification.get("uniformVerifiers") %}
|
|
104
|
+
<p>{{ t('tasks.implementation-planning.uniform-verifier-label') }} — {% for row in planning.planBodyVerification.uniformVerifiers %}{{ row.worker | inline_code }} · {{ row.verdict | inline_code }} ({{ row.itemCount }}){% if not loop.last %}, {% endif %}{% endfor %}</p>
|
|
105
|
+
<p class="evidence-refs">{{ t('tasks.implementation-planning.uniform-verifier-legend') }}</p>
|
|
106
|
+
{% endif %}
|
|
107
|
+
<div class="summary-grid">{% for item in planning.planBodyVerification.planItems %}<article class="summary-card" id="id-{{ item.id }}"><p class="eyebrow">{{ item.id | inline_code }}{% if item.get("sourceSection") %} · {{ t('tasks.implementation-planning.source-section') }} {{ item.sourceSection | inline_code }}{% endif %}</p><h3>{{ item.subject | inline_code }}</h3>{% for vote in item.verdicts %}<p><strong>{{ vote.worker | inline_code }}</strong> <span class="status status-{{ vote.verdict | lower }}">{{ vote.verdict }}</span>{% if vote.get("breakageKind") %} · {{ t('tasks.implementation-planning.breakage') }} {{ vote.breakageKind | inline_code }}{% endif %}{% if vote.get("fixability") %} · {{ t('tasks.implementation-planning.fixability') }} {{ vote.fixability | inline_code }}{% endif %}{% if vote.get("claimKind") %} · {{ t('tasks.implementation-planning.claim') }} {{ vote.claimKind | inline_code }}{% endif %}{% if vote.get("reproductionResult") %} · {{ t('tasks.implementation-planning.reproduction') }} {{ vote.reproductionResult | inline_code }}{% endif %}</p>{% if vote.get("note") %}<p class="evidence-refs">{{ t('tasks.implementation-planning.note') }} {{ vote.note | inline_code }}</p>{% endif %}{% if vote.get("explanation") %}<p>{{ vote.explanation | inline_code }}</p>{% endif %}{% endfor %}</article>{% endfor %}</div>
|
|
108
|
+
{% if planning.planBodyVerification.get("dissentLog") %}
|
|
109
|
+
<h3>{{ t('tasks.implementation-planning.dissent-left-standing') }}</h3>
|
|
110
|
+
<ul>{% for row in planning.planBodyVerification.dissentLog %}<li>{{ row.planItem | inline_code }} · {{ row.workerRole | inline_code }} — {{ row.body | inline_code }}</li>{% endfor %}</ul>
|
|
111
|
+
{% endif %}
|
|
112
|
+
</section>
|
|
113
|
+
{% endif %}
|
|
114
|
+
|
|
105
115
|
{% if planning.get("supersessionLedger") is not none %}
|
|
106
116
|
<section data-report-section="superseded" data-report-field="implementationPlanning.supersessionLedger">
|
|
107
117
|
<h2>{{ t('tasks.implementation-planning.what-your-answers-overturned') }}</h2>
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
<h2>{{ t('tasks.implementation.verification-result') }}</h2>
|
|
25
25
|
{{ render_narrative(narrative.validationExplanation, "implementation.userNarrative.validationExplanation") }}
|
|
26
26
|
<div class="summary-grid">{% for row in implementation.validationEvidence %}{{ summary_card(row.phase ~ " · " ~ t('tasks.implementation.exit-code') ~ " " ~ row.exitCode, row.outputTail, "important" if row.exitCode else "neutral") }}{% endfor %}</div>
|
|
27
|
-
<p><strong>{{ t('tasks.implementation.independent-
|
|
27
|
+
{% for row in implementation.verifierResults %}<article class="summary-card tone-{{ row.verdict | lower }}"><p class="eyebrow">{{ row.verifier | inline_code }} · <span class="status status-{{ row.verdict | lower }}">{{ row.verdict }}</span></p>{% if row.get("independentValidationRerun") %}<p><strong>{{ t('tasks.implementation.independent-rerun') }}</strong> {{ row.independentValidationRerun | inline_code }}</p>{% endif %}{% if row.get("readOnlyCommandLog") %}<p><strong>{{ t('tasks.implementation.command-log') }}</strong> {{ row.readOnlyCommandLog | inline_code }}</p>{% endif %}{% if row.get("discrepancy") %}<p class="evidence-refs">{{ t('tasks.implementation.discrepancy') }} {{ row.discrepancy | inline_code }}</p>{% endif %}{% if row.get("declinedFixRecommendations") %}<p>{{ row.declinedFixRecommendations | inline_code }}</p>{% endif %}</article>{% endfor %}
|
|
28
28
|
</section>
|
|
29
29
|
|
|
30
30
|
<section data-report-section="remaining-issues">
|