okstra 0.185.1 → 0.186.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 (33) hide show
  1. package/dist/commands/chat/chat.mjs +60 -14
  2. package/dist/commands/chat/chat.mjs.map +1 -1
  3. package/docs/cli.md +1 -1
  4. package/docs/for-ai/skills/okstra-chat.md +4 -4
  5. package/package.json +1 -1
  6. package/runtime/BUILD.json +2 -2
  7. package/runtime/prompts/lead/okstra-lead-contract.md +1 -1
  8. package/runtime/prompts/profiles/implementation-planning.md +1 -1
  9. package/runtime/python/okstra_ctl/cmux.py +63 -35
  10. package/runtime/python/okstra_ctl/report_html/common.py +86 -11
  11. package/runtime/python/okstra_ctl/report_html/filters.py +27 -10
  12. package/runtime/python/okstra_ctl/report_html/render.py +1 -0
  13. package/runtime/python/okstra_ctl/report_html/report_index.py +5 -1
  14. package/runtime/python/okstra_ctl/report_html/view_models/implementation_planning.py +1 -1
  15. package/runtime/skills/okstra-chat/SKILL.md +10 -2
  16. package/runtime/templates/reports/html/assets/base.css +48 -3
  17. package/runtime/templates/reports/html/base.template.html +7 -4
  18. package/runtime/templates/reports/html/i18n/en.json +86 -8
  19. package/runtime/templates/reports/html/i18n/ko.json +86 -8
  20. package/runtime/templates/reports/html/macros/forms.html +74 -55
  21. package/runtime/templates/reports/html/macros/layout.html +2 -2
  22. package/runtime/templates/reports/html/macros/visualizations.html +1 -1
  23. package/runtime/templates/reports/html/tasks/change-impact-analysis.template.html +2 -2
  24. package/runtime/templates/reports/html/tasks/error-analysis.template.html +3 -3
  25. package/runtime/templates/reports/html/tasks/feature-analysis.template.html +4 -4
  26. package/runtime/templates/reports/html/tasks/final-verification.template.html +3 -3
  27. package/runtime/templates/reports/html/tasks/implementation-option-selection.template.html +9 -9
  28. package/runtime/templates/reports/html/tasks/implementation-planning.template.html +20 -18
  29. package/runtime/templates/reports/html/tasks/implementation.template.html +3 -3
  30. package/runtime/templates/reports/html/tasks/improvement-discovery.template.html +3 -3
  31. package/runtime/templates/reports/html/tasks/project-analysis.template.html +10 -10
  32. package/runtime/templates/reports/html/tasks/release-handoff.template.html +2 -2
  33. package/runtime/templates/reports/html/tasks/requirements-discovery.template.html +4 -4
@@ -28,16 +28,16 @@
28
28
  <h3>{{ t('tasks.project-analysis.language') }}</h3>
29
29
  <table><thead><tr><th>{{ t('tasks.project-analysis.language') }}</th><th>{{ t('tasks.project-analysis.version') }}</th><th>{{ t('tasks.project-analysis.version-source') }}</th><th>{{ t('tasks.project-analysis.files') }}</th><th>{{ t('tasks.project-analysis.lines') }}</th></tr></thead><tbody>{% for row in project.techStack.languages %}<tr><td>{{ row.name | inline_code }}</td><td><code>{{ row.version }}</code></td><td>{{ row.versionSource | enum_label("versionSource") }}</td><td>{{ row.get("fileCount") | default("--") }}</td><td>{{ row.get("lineCount") | default("--") }}</td></tr>{% endfor %}</tbody></table>
30
30
  {% if project.techStack.get("frameworks") %}<h3>{{ t('tasks.project-analysis.framework') }}</h3>
31
- <table><thead><tr><th>{{ t('tasks.project-analysis.framework') }}</th><th>{{ t('tasks.project-analysis.role') }}</th><th>{{ t('tasks.project-analysis.evidence') }}</th></tr></thead><tbody>{% for row in project.techStack.frameworks %}<tr>{{ row_key(pairs=[("Name", row.name), ("Version", row.version), ("Version source", row.versionSource | enum_label("versionSource"))]) }}<td>{{ row.role | inline_code }}</td><td>{{ row.get("currentCodeEvidence") | default([]) | code_evidence }}</td></tr>{% endfor %}</tbody></table>{% endif %}
31
+ <table><thead><tr><th>{{ t('tasks.project-analysis.framework') }}</th><th>{{ t('tasks.project-analysis.role') }}</th><th>{{ t('tasks.project-analysis.evidence') }}</th></tr></thead><tbody>{% for row in project.techStack.frameworks %}<tr>{{ row_key(pairs=[(t('macros.layout.name'), row.name), (t('macros.layout.version'), row.version), (t('macros.layout.version-source'), row.versionSource | enum_label("versionSource"))]) }}<td>{{ row.role | inline_code }}</td><td>{{ row.get("currentCodeEvidence") | default([]) | code_evidence }}</td></tr>{% endfor %}</tbody></table>{% endif %}
32
32
  {% if project.techStack.get("toolchain") %}<h3>{{ t('tasks.project-analysis.tool') }}</h3>
33
- <table><thead><tr><th>{{ t('tasks.project-analysis.tool') }}</th><th>{{ t('tasks.project-analysis.command') }}</th></tr></thead><tbody>{% for row in project.techStack.toolchain %}<tr>{{ row_key(pairs=[("Name", row.name), ("Kind", row.kind | enum_label("toolKind")), ("Version", row.get("version"))]) }}<td>{% if row.get("command") %}<code>{{ row.command }}</code>{% else %}--{% endif %}</td></tr>{% endfor %}</tbody></table>{% endif %}
33
+ <table><thead><tr><th>{{ t('tasks.project-analysis.tool') }}</th><th>{{ t('tasks.project-analysis.command') }}</th></tr></thead><tbody>{% for row in project.techStack.toolchain %}<tr>{{ row_key(pairs=[(t('macros.layout.name'), row.name), (t('macros.layout.kind'), row.kind | enum_label("toolKind")), (t('macros.layout.version'), row.get("version"))]) }}<td>{% if row.get("command") %}<code>{{ row.command }}</code>{% else %}--{% endif %}</td></tr>{% endfor %}</tbody></table>{% endif %}
34
34
  </section>
35
35
  {% endif %}
36
36
 
37
37
  {% if project.get("internalInterfaces") %}
38
38
  <section data-report-section="internal-interfaces" data-report-field="projectAnalysis.internalInterfaces">
39
39
  <h2>{{ t('tasks.project-analysis.internal-interfaces') }}</h2>
40
- <table><thead><tr><th>{{ t('tasks.project-analysis.interface') }}</th><th>{{ t('tasks.project-analysis.signature') }}</th><th>{{ t('tasks.project-analysis.consumers') }}</th><th>{{ t('tasks.project-analysis.evidence') }}</th></tr></thead><tbody>{% for row in project.internalInterfaces %}<tr id="id-{{ row.id }}">{{ row_key(pairs=[("ID", row.id), ("Name", row.name), ("Kind", row.kind | enum_label("interfaceKind")), ("Owner", row.ownerComponentId)]) }}<td><code>{{ row.signature }}</code></td><td>{{ (row.get("consumers") | default([])) | join(", ") | inline_code }}</td><td>{{ row.get("currentCodeEvidence") | default([]) | code_evidence }}</td></tr>{% endfor %}</tbody></table>
40
+ <table><thead><tr><th>{{ t('tasks.project-analysis.interface') }}</th><th>{{ t('tasks.project-analysis.signature') }}</th><th>{{ t('tasks.project-analysis.consumers') }}</th><th>{{ t('tasks.project-analysis.evidence') }}</th></tr></thead><tbody>{% for row in project.internalInterfaces %}<tr id="id-{{ row.id }}">{{ row_key(pairs=[(t('macros.layout.id'), row.id), (t('macros.layout.name'), row.name), (t('macros.layout.kind'), row.kind | enum_label("interfaceKind")), (t('macros.layout.owner'), row.ownerComponentId)]) }}<td><code>{{ row.signature }}</code></td><td>{{ (row.get("consumers") | default([])) | join(", ") | inline_code }}</td><td>{{ row.get("currentCodeEvidence") | default([]) | code_evidence }}</td></tr>{% endfor %}</tbody></table>
41
41
  </section>
42
42
  {% endif %}
43
43
 
@@ -47,7 +47,7 @@
47
47
  {{ figure(workflowFigure) }}
48
48
  {% for flow in project.workflows %}
49
49
  <article class="workflow" id="id-{{ flow.id }}">
50
- <p class="eyebrow">{{ flow.id }}</p>
50
+ <p class="eyebrow">{{ flow.id | inline_code }}</p>
51
51
  <h3>{{ flow.name | inline_code }}</h3>
52
52
  <p><strong>{{ t('tasks.project-analysis.starts-with') }}</strong> — {{ flow.trigger | inline_code }}{% if flow.get("outcome") %} · <strong>{{ t('tasks.project-analysis.result') }}</strong> — {{ flow.outcome | inline_code }}{% endif %}</p>
53
53
  <table><thead><tr><th>{{ t('tasks.project-analysis.order') }}</th><th>{{ t('tasks.project-analysis.action') }}</th><th>{{ t('tasks.project-analysis.component') }}</th><th>{{ t('tasks.project-analysis.external-system') }}</th></tr></thead><tbody>{% for step in flow.steps %}<tr><td>{{ step.order }}</td><td>{{ step.action | inline_code }}</td><td>{{ step.componentId | inline_code }}</td><td>{% if step.get("externalSystem") %}{{ step.externalSystem | inline_code }}{% else %}--{% endif %}</td></tr>{% endfor %}</tbody></table>
@@ -60,11 +60,11 @@
60
60
  <h2>{{ t('tasks.project-analysis.system-boundaries') }}</h2>
61
61
  <p class="scan-coverage" data-report-field="projectAnalysis.scanCoverage"><strong>{{ project.scanCoverage.scannedPathCount }}</strong> {{ t('tasks.project-analysis.paths-read') }} <strong>{{ project.scanCoverage.unscannedPathCount }}</strong> left unread — {{ project.scanCoverage.reason | inline_code }}</p>
62
62
  <h3>{{ t('tasks.project-analysis.entry-point') }}</h3>
63
- <table data-report-field="projectAnalysis.entryPoints"><thead><tr><th>{{ t('tasks.project-analysis.entry-point') }}</th><th>{{ t('tasks.project-analysis.path') }}</th><th>{{ t('tasks.project-analysis.role') }}</th></tr></thead><tbody>{% for row in project.entryPoints %}<tr>{{ row_key(pairs=[("Symbol", row.symbol), ("Kind", row.kind)]) }}<td><code>{{ row.path }}</code></td><td>{{ row.role | inline_code }}</td></tr>{% endfor %}</tbody></table>
63
+ <table data-report-field="projectAnalysis.entryPoints"><thead><tr><th>{{ t('tasks.project-analysis.entry-point') }}</th><th>{{ t('tasks.project-analysis.path') }}</th><th>{{ t('tasks.project-analysis.role') }}</th></tr></thead><tbody>{% for row in project.entryPoints %}<tr>{{ row_key(pairs=[(t('macros.layout.symbol'), row.symbol), (t('macros.layout.kind'), row.kind)]) }}<td><code>{{ row.path }}</code></td><td>{{ row.role | inline_code }}</td></tr>{% endfor %}</tbody></table>
64
64
  <h3>{{ t('tasks.project-analysis.data-stores') }}</h3>
65
- <table data-report-field="projectAnalysis.dataStores"><thead><tr><th>{{ t('tasks.project-analysis.store') }}</th><th>{{ t('tasks.project-analysis.reads') }}</th><th>{{ t('tasks.project-analysis.writes') }}</th><th>{{ t('tasks.project-analysis.evidence') }}</th></tr></thead><tbody>{% for row in project.dataStores %}<tr>{{ row_key(pairs=[("Name", row.name), ("Owner", row.ownerComponentId)]) }}<td>{{ row.readBoundary | inline_code }}</td><td>{{ row.writeBoundary | inline_code }}</td><td>{{ row.currentCodeEvidence | code_evidence }}</td></tr>{% endfor %}</tbody></table>
65
+ <table data-report-field="projectAnalysis.dataStores"><thead><tr><th>{{ t('tasks.project-analysis.store') }}</th><th>{{ t('tasks.project-analysis.reads') }}</th><th>{{ t('tasks.project-analysis.writes') }}</th><th>{{ t('tasks.project-analysis.evidence') }}</th></tr></thead><tbody>{% for row in project.dataStores %}<tr>{{ row_key(pairs=[(t('macros.layout.name'), row.name), (t('macros.layout.owner'), row.ownerComponentId)]) }}<td>{{ row.readBoundary | inline_code }}</td><td>{{ row.writeBoundary | inline_code }}</td><td>{{ row.currentCodeEvidence | code_evidence }}</td></tr>{% endfor %}</tbody></table>
66
66
  <h3>{{ t('tasks.project-analysis.external-system') }}</h3>
67
- <table data-report-field="projectAnalysis.externalSystems"><thead><tr><th>{{ t('tasks.project-analysis.external-system') }}</th><th>{{ t('tasks.project-analysis.evidence') }}</th></tr></thead><tbody>{% for row in project.externalSystems %}<tr>{{ row_key(pairs=[("Name", row.name), ("Adapter", row.adapter), ("Direction", row.direction)]) }}<td>{{ row.currentCodeEvidence | code_evidence }}</td></tr>{% endfor %}</tbody></table>
67
+ <table data-report-field="projectAnalysis.externalSystems"><thead><tr><th>{{ t('tasks.project-analysis.external-system') }}</th><th>{{ t('tasks.project-analysis.evidence') }}</th></tr></thead><tbody>{% for row in project.externalSystems %}<tr>{{ row_key(pairs=[(t('macros.layout.name'), row.name), (t('macros.layout.adapter'), row.adapter), (t('macros.layout.direction'), row.direction)]) }}<td>{{ row.currentCodeEvidence | code_evidence }}</td></tr>{% endfor %}</tbody></table>
68
68
  </section>
69
69
 
70
70
  <section data-report-section="hotspots" data-report-field="projectAnalysis.rankedHotspots">
@@ -72,7 +72,7 @@
72
72
  {{ render_narrative(narrative.hotspotExplanation, "projectAnalysis.userNarrative.hotspotExplanation") }}
73
73
  <div class="summary-grid">
74
74
  {% for row in project.rankedHotspots %}
75
- <article class="summary-card tone-{{ row.risk }}" id="id-{{ row.id }}"><p class="eyebrow">{{ row.id }} · Impact {{ row.impact }} · Risk {{ row.risk }}</p><h3>{{ row.title | inline_code }}</h3><p>{{ row.reason | inline_code }}</p><p class="evidence-refs">Evidence: {{ row.evidenceRefs | evidence_refs }}</p></article>
75
+ <article class="summary-card tone-{{ row.risk }}" id="id-{{ row.id }}"><p class="eyebrow">{{ row.id | inline_code }} · {{ t('macros.layout.impact') }} {{ row.impact }} · {{ t('macros.layout.risk') }} {{ row.risk }}</p><h3>{{ row.title | inline_code }}</h3><p>{{ row.reason | inline_code }}</p><p class="evidence-refs">{{ t('macros.layout.evidence') }} {{ row.evidenceRefs | evidence_refs }}</p></article>
76
76
  {% endfor %}
77
77
  </div>
78
78
  </section>
@@ -80,13 +80,13 @@
80
80
  <section data-report-section="quality-and-tests" data-report-field="projectAnalysis.qualityCoverage">
81
81
  <h2>{{ t('tasks.project-analysis.quality-and-test-coverage') }}</h2>
82
82
  {{ render_narrative(narrative.qualityExplanation, "projectAnalysis.userNarrative.qualityExplanation") }}
83
- <table><thead><tr><th>{{ t('tasks.project-analysis.area') }}</th><th>{{ t('tasks.project-analysis.detail') }}</th><th>{{ t('tasks.project-analysis.evidence') }}</th></tr></thead><tbody>{% for row in project.qualityCoverage %}<tr id="id-{{ row.id }}">{{ row_key(pairs=[("ID", row.id), ("Area", row.area)], status_name="Status", status_raw=row.status, status_text=row.status | enum_label("coverage")) }}<td>{{ row.summary | inline_code }}</td><td>{{ row.evidenceRefs | evidence_refs }}</td></tr>{% endfor %}</tbody></table>
83
+ <table><thead><tr><th>{{ t('tasks.project-analysis.area') }}</th><th>{{ t('tasks.project-analysis.detail') }}</th><th>{{ t('tasks.project-analysis.evidence') }}</th></tr></thead><tbody>{% for row in project.qualityCoverage %}<tr id="id-{{ row.id }}">{{ row_key(pairs=[(t('macros.layout.id'), row.id), (t('macros.layout.area'), row.area)], status_name=t('macros.layout.status'), status_raw=row.status, status_text=row.status | enum_label("coverage")) }}<td>{{ row.summary | inline_code }}</td><td>{{ row.evidenceRefs | evidence_refs }}</td></tr>{% endfor %}</tbody></table>
84
84
  <dl class="enum-legend">{% for label, hint in "coverage" | enum_legend %}<dt>{{ label }}</dt><dd>{{ hint }}</dd>{% endfor %}</dl>
85
85
  </section>
86
86
 
87
87
  <section data-report-section="feature-map" data-report-field="projectAnalysis.featureIndex">
88
88
  <h2>{{ t('tasks.project-analysis.feature-map') }}</h2>
89
- <div class="summary-grid">{% for row in project.featureIndex %}{{ summary_card(row.id ~ " · " ~ (row.name | inline_code), row.description, anchor=row.id) }}{% endfor %}</div>
89
+ <div class="summary-grid">{% for row in project.featureIndex %}{{ summary_card(row.id ~ " · " ~ row.name, row.description, anchor=row.id) }}{% endfor %}</div>
90
90
  </section>
91
91
 
92
92
  <section data-report-section="user-actions">
@@ -31,14 +31,14 @@
31
31
  <section data-report-section="conflicts" data-report-field="releaseHandoff.mergeConflictProbe">
32
32
  <h2>{{ t('tasks.release-handoff.conflict-verdict') }}</h2>
33
33
  {{ render_narrative(narrative.conflictExplanation, "releaseHandoff.userNarrative.conflictExplanation") }}
34
- {{ summary_card(handoff.mergeConflictProbe.kind, "Base branch: " ~ (handoff.mergeConflictProbe.baseBranch | default('not-run'))) }}
34
+ {{ summary_card(handoff.mergeConflictProbe.kind, t('tasks.release-handoff.base-branch') ~ " " ~ (handoff.mergeConflictProbe.baseBranch | default('not-run'))) }}
35
35
  {% if handoff.mergeConflictProbe.conflictingPaths %}<ul>{% for path in handoff.mergeConflictProbe.conflictingPaths %}<li>{{ path }}</li>{% endfor %}</ul>{% endif %}
36
36
  </section>
37
37
 
38
38
  <section data-report-section="delivered-commits" data-report-field="releaseHandoff.commitList">
39
39
  <h2>{{ t('tasks.release-handoff.commits-delivered') }}</h2>
40
40
  {% if handoff.commitList is mapping %}<p>{{ t('tasks.release-handoff.there-are-no-commits') }}</p>{% else %}
41
- <table><thead><tr><th>{{ t('tasks.release-handoff.commit') }}</th><th>{{ t('tasks.release-handoff.subject') }}</th><th>{{ t('tasks.release-handoff.files') }}</th></tr></thead><tbody>{% for row in handoff.commitList %}<tr>{{ row_key(pairs=[("SHA", row.shortSha), ("Order", loop.index)]) }}<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 %}
41
+ <table><thead><tr><th>{{ t('tasks.release-handoff.commit') }}</th><th>{{ t('tasks.release-handoff.subject') }}</th><th>{{ t('tasks.release-handoff.files') }}</th></tr></thead><tbody>{% for row in handoff.commitList %}<tr>{{ row_key(pairs=[(t('macros.layout.sha'), row.shortSha), (t('macros.layout.order'), loop.index)]) }}<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 %}
42
42
  {% if handoff.sourceVerificationReports %}<p data-report-field="releaseHandoff.sourceVerificationReports"><strong>{{ t('tasks.release-handoff.the-verification-report-this-rests-on') }}</strong>{% for row in handoff.sourceVerificationReports %} — stage {{ row.stage }}: <code>{{ row.path }}</code> · {{ row.verdictTokenQuote | inline_code }}{% endfor %}</p>
43
43
  {% else %}<p data-report-field="releaseHandoff.sourceVerificationReport"><strong>{{ t('tasks.release-handoff.the-verification-report-this-rests-on') }}</strong> — <code>{{ handoff.sourceVerificationReport.path }}</code> · {{ handoff.sourceVerificationReport.verdictTokenQuote | inline_code }}</p>{% endif %}
44
44
  </section>
@@ -20,7 +20,7 @@
20
20
  <p>{{ t('tasks.requirements-discovery.the-requester-left-no-rejection-criteria-eve') }}</p>
21
21
  {% else %}
22
22
  <p>{{ requirements.rejectionCriteria.get("statement") | inline_code }}</p>
23
- <p class="evidence-refs">Source: {{ requirements.rejectionCriteria.source | enum_label("rejectionSource") }}{% if requirements.rejectionCriteria.get("clarificationId") %} · {{ requirements.rejectionCriteria.clarificationId | inline_code }}{% endif %}{% if requirements.rejectionCriteria.get("evidenceRefs") %} · {{ requirements.rejectionCriteria.evidenceRefs | evidence_refs }}{% endif %}</p>
23
+ <p class="evidence-refs">{{ t('macros.layout.source') }} {{ requirements.rejectionCriteria.source | enum_label("rejectionSource") }}{% if requirements.rejectionCriteria.get("clarificationId") %} · {{ requirements.rejectionCriteria.clarificationId | inline_code }}{% endif %}{% if requirements.rejectionCriteria.get("evidenceRefs") %} · {{ requirements.rejectionCriteria.evidenceRefs | evidence_refs }}{% endif %}</p>
24
24
  {% endif %}
25
25
  </section>
26
26
  {% endif %}
@@ -28,7 +28,7 @@
28
28
  {% if requirements.get("domainAlignment") %}
29
29
  <section data-report-section="domain-alignment" data-report-field="requirementsDiscovery.domainAlignment">
30
30
  <h2>{{ t('tasks.requirements-discovery.terms-settled') }}</h2>
31
- <p>Glossary {{ "checked" if requirements.domainAlignment.glossaryConsulted else "none" }} · Decision record {{ "checked" if requirements.domainAlignment.decisionsConsulted else "none" }}</p>
31
+ <p>{{ t('tasks.requirements-discovery.glossary') }} {{ t('macros.layout.checked') if requirements.domainAlignment.glossaryConsulted else t('macros.layout.none') }} · {{ t('tasks.requirements-discovery.decision-record') }} {{ t('macros.layout.checked') if requirements.domainAlignment.decisionsConsulted else t('macros.layout.none') }}</p>
32
32
  <table><thead><tr><th>{{ t('tasks.requirements-discovery.as-the-brief-put-it') }}</th><th>{{ t('tasks.requirements-discovery.settled-term') }}</th><th>{{ t('tasks.requirements-discovery.settled-by') }}</th><th>{{ t('tasks.requirements-discovery.evidence') }}</th></tr></thead><tbody>{% for row in requirements.domainAlignment.terms %}<tr><td>{{ row.raw | inline_code }}</td><td>{{ row.canonical | inline_code }}</td><td>{{ row.resolvedBy | enum_label("termSource") }}</td><td>{{ row.get("evidenceRefs") | default([]) | evidence_refs }}</td></tr>{% else %}<tr><td colspan="4">{{ t('tasks.requirements-discovery.no-term-was-ambiguous') }}</td></tr>{% endfor %}</tbody></table>
33
33
  </section>
34
34
  {% endif %}
@@ -36,9 +36,9 @@
36
36
  <section data-report-section="requirements">
37
37
  <h2>{{ t('tasks.requirements-discovery.settled-and-unsettled-requirements') }}</h2>
38
38
  {{ render_narrative(narrative.confirmedRequirements, "requirementsDiscovery.userNarrative.confirmedRequirements") }}
39
- <div class="summary-grid" data-report-field="requirementsDiscovery.confirmedRequirements">{% for row in requirements.confirmedRequirements %}{{ summary_card(row.id ~ " · Settled", row.statement, anchor=row.id) }}{% endfor %}</div>
39
+ <div class="summary-grid" data-report-field="requirementsDiscovery.confirmedRequirements">{% for row in requirements.confirmedRequirements %}{{ summary_card(row.id ~ " · " ~ t('tasks.requirements-discovery.settled'), row.statement, anchor=row.id) }}{% endfor %}</div>
40
40
  {{ render_narrative(narrative.unresolvedRequirements, "requirementsDiscovery.userNarrative.unresolvedRequirements") }}
41
- <div class="summary-grid" data-report-field="requirementsDiscovery.unresolvedRequirements">{% for row in requirements.unresolvedRequirements %}{{ summary_card(row.id ~ " · Decision needed", row.statement, "important") }}{% else %}<p>{{ t('tasks.requirements-discovery.nothing-is-left-unsettled') }}</p>{% endfor %}</div>
41
+ <div class="summary-grid" data-report-field="requirementsDiscovery.unresolvedRequirements">{% for row in requirements.unresolvedRequirements %}{{ summary_card(row.id ~ " · " ~ t('tasks.requirements-discovery.decision-needed'), row.statement, "important") }}{% else %}<p>{{ t('tasks.requirements-discovery.nothing-is-left-unsettled') }}</p>{% endfor %}</div>
42
42
  </section>
43
43
 
44
44
  <section data-report-section="routing">