{# Executive Summary — Phase 1a briefing-shell migration. Extends _briefing_shell.html (T0b deliverable). The shell owns DOCTYPE/html/head/body, the render_mode chrome gate, the 880px page frame, the design-system include, the readiness beacons, and the Inter font load. This template fills the named blocks only. Chart story (per design spec § Per-recipe chart story): - distribution → project breakdown bar (stacked by severity) - sections → open issues pie + scan frequency line + table - stats → 4 derived KPI cards (this plan's deviation from the spec table; see plan's Reconciliation note) Chart blocks branch on render_mode (the canonical Option-Y pattern from fs_report/templates/bar_chart.html): - fragment mode → emit server_svgs.get('', '') | safe - HTML/PDF mode → emit namespaced container
+ ECharts {% endblock %} {# briefing_header inherits the shell's default chrome — logo + meta band (recipe name, period, generated_at, record count, folder path, project name). Override here only if executive_summary needs different chrome; the default covers exec_summary's needs today. #} {% block hero %} {# Hero strip = eyebrow + section title + deck. In compound bundles the assembler emits its own SECTION 0N divider with the same title immediately above the fragment, so the entire hero is the chrome to suppress (not just the H1). The wrapping
below carries the suppress-marker so render_fragment(..., suppress_section_title=True) removes the whole block. Single-recipe fragment consumers (forge MCP renders) keep the hero. (B1.4 follow-up: fragment-mode visual parity.) #}
Executive Summary

{{ slide_title | default("Executive Security Summary") }}

{{ description | default("High-level security posture overview with key performance indicators, risk metrics, and project-level breakdowns.") }}

{% endblock %} {% block stats %} {# Derive 4 KPI cards from table_data totals. table_data.rows is a list of per-project dicts with critical/high/medium/low integer columns (post-pivot from the recipe's main transform). Jinja namespace-mutation syntax: {% set ns.attr = ... %} after {% set ns = namespace(attr=...) %}. NOT ns.update({...}) — Jinja namespaces don't expose Python dict methods. Same pattern as fs_report/templates/security_progress.html:199. #} {% set ns = namespace(c=0, h=0, m=0, l=0) %} {% if table_data and table_data.rows %} {% for row in table_data.rows %} {% set ns.c = ns.c + (row.get('critical', 0) or 0) | int %} {% set ns.h = ns.h + (row.get('high', 0) or 0) | int %} {% set ns.m = ns.m + (row.get('medium', 0) or 0) | int %} {% set ns.l = ns.l + (row.get('low', 0) or 0) | int %} {% endfor %} {% endif %} {% set total_findings = ns.c + ns.h + ns.m + ns.l %}
Total Findings
{{ total_findings }}
Critical
{{ ns.c }}
High
{{ ns.h }}
Medium + Low
{{ ns.m + ns.l }}
{% endblock %} {% block distribution %}

{{ project_breakdown_spec.title if project_breakdown_spec else "Security Findings by Project and Severity" }}

{% if project_breakdown_spec %}

{{ project_breakdown_spec.description }}

{% endif %} {% if render_mode == 'fragment' and not fragment_scripts_enabled %}
{{ server_svgs.get('project_breakdown', '') | safe }}
{% else %} {# Container only — all five Exec Summary charts are initialized by the consolidated themed script at the end of the sections block (it owns token-driven text colors + re-theme on light/dark toggle, A3). #}
{% endif %}
{% endblock %} {% block sections %}
{# 2×2 chart grid. Row 1: Open Issues by Severity (pie) + Exploit Signals (gauge, C1) Row 2: Security Findings Over Time (line) + Exploits Over Time (line, C2) Each cell branches on render_mode independently; fragment mode emits a per-chart server SVG, HTML/PDF emits an ECharts container. All five containers are initialized by the consolidated themed script below. #}

{{ open_issues_spec.title if open_issues_spec else "Open Issues by Severity" }}

{% if open_issues_spec %}

{{ open_issues_spec.description }}

{% endif %} {% if render_mode == 'fragment' and not fragment_scripts_enabled %}
{{ server_svgs.get('open_issues_distribution', '') | safe }}
{% else %}
{% endif %}

{{ exploit_signals_spec.title if exploit_signals_spec else "Exploit Signals" }}

{% if exploit_signals_spec %}

{{ exploit_signals_spec.description }}

{% endif %} {% if render_mode == 'fragment' and not fragment_scripts_enabled %}
{{ server_svgs.get('exploit_signals', '') | safe }}
{% else %}
{% endif %}

{{ scan_frequency_spec.title if scan_frequency_spec else "Security Findings Over Time" }}

{% if scan_frequency_spec %}

{{ scan_frequency_spec.description }}

{% endif %} {% if render_mode == 'fragment' and not fragment_scripts_enabled %}
{{ server_svgs.get('scan_frequency', '') | safe }}
{% else %}
{% endif %}

{{ exploits_over_time_spec.title if exploits_over_time_spec else "Exploits Over Time" }}

{% if exploits_over_time_spec %}

{{ exploits_over_time_spec.description }}

{% endif %} {% if render_mode == 'fragment' and not fragment_scripts_enabled %}
{{ server_svgs.get('exploits_over_time', '') | safe }}
{% else %}
{% endif %}
{% if render_mode != 'fragment' or fragment_scripts_enabled %} {% endif %} {# Findings by Project table — preserves the legacy pivoted summary. Renders in all modes; table HTML survives fragment extraction. #} {% if table_data and table_data.rows %}

Findings by Project

{% for row in table_data.rows %} {% set crit = row.get('critical', 0) | default(0, true) | int %} {% set high = row.get('high', 0) | default(0, true) | int %} {% set med = row.get('medium', 0) | default(0, true) | int %} {% set low = row.get('low', 0) | default(0, true) | int %} {% endfor %}
Project Critical High Medium Low Total
{{ row.get('project.name', '') }} {% if crit > 0 %}{{ crit }}{% else %}0{% endif %} {% if high > 0 %}{{ high }}{% else %}0{% endif %} {% if med > 0 %}{{ med }}{% else %}0{% endif %} {{ low }} {{ crit + high + med + low }}
{% endif %}
{% endblock %} {% block methodology %}

This briefing summarizes the security findings across the portfolio for the reporting period. KPIs at the top reflect totals across all findings (open + resolved) aggregated from the main transform. The distribution chart breaks those totals down by project and severity. The open-issues pie counts only unresolved findings (status not in RESOLVED, RESOLVED_WITH_PEDIGREE, NOT_AFFECTED, FALSE_POSITIVE) — different population from the KPIs above. The scan-frequency line tracks the count of findings detected per period.

{% endblock %}