{# Vendored layout from packages/web/src/governor_web/templates/project/opportunity_detail.html (the page governor-cli boots locally for /opportunities/{id}). The solution / PR / dry-run / shadow / clearing surfaces are deliberately absent — audit is read-only and never generates solutions — but the page header, breadcrumb, ui-metric-grid stat strip, evidence section, and source-SQL viewer mirror the cloud's grammar 1:1. #} {% extends "layout.html" %} {% block content %}
Opportunities {% if row.dbt_model_name %}{{ row.dbt_model_name }}{% else %}{{ row.opportunity_type | replace('_', ' ') }}{% endif %}

Recommendation

{% if row.dbt_model_name %}

{{ row.dbt_model_name }}

{% else %}

{{ row.affected_table }}

{% endif %}
{{ row.opportunity_type | replace('_', ' ') }} {% if row.severity_bucket == 'critical' %} Severity {{ row.severity_score }} · critical {% elif row.severity_bucket == 'high' %} Severity {{ row.severity_score }} · high {% elif row.severity_bucket == 'medium' %} Severity {{ row.severity_score }} · medium {% else %} Severity {{ row.severity_score }} · low {% endif %} {% if row.dbt_model_name %} dbt {% endif %}

{% if row.dbt_model_name %} {{ row.affected_table }} {% else %} {{ row.opportunity_type | replace('_', ' ') }} {% endif %}

{# Stat strip — matches the /jobs/{id} job-detail layout exactly so the user sees the same four cards (Type → Cost → Bytes Processed → Slot Time → Statement Type) on both pages. The cloud's ui-metric-grid was replaced with ui-stat-card here for parity. #}

Type

{{ row.opportunity_type | replace('_', ' ') }}

Cost

{{ row.query_cost_value | format_usd }}

Bytes Processed

{% if job and job.total_bytes_processed is not none %}

{{ job.total_bytes_processed | format_bytes }}

{% if job.total_bytes_billed is not none %}

billed: {{ job.total_bytes_billed | format_bytes }}

{% endif %} {% else %}

{% endif %}

Slot Time (hours)

{% if job and job.total_slot_ms is not none %}

{{ "%.1f" | format((job.total_slot_ms or 0) / 3600000) }}

{% if job.duration_ms is not none %}

duration: {{ job.duration_ms | format_duration }}

{% endif %} {% else %}

{% endif %}
{# Why this fired + Recommended fix — mirror the cloud's "explanation" + "Solution" pair #}

Why this fired

{{ row.explanation }}

{% if rule_meta and rule_meta.description %}

{{ rule_meta.description }}

{% endif %}

Recommended fix

{{ row.explanation }}

Audit is read-only — it identifies issues but doesn't generate SQL rewrites or open pull requests. Run governor-cli against the same project for the LLM-generated solution + dry-run + PR pipeline.

{# Source SQL — same code-viewer pattern audit's job_detail.html uses #} {% if job and job.query %}
Source SQL · job {{ job.job_id }} view job → {{ job.query | length }} chars
{% for line in job.query.split('\n') %}{{ loop.index }}{{ line }}
{% endfor %}
{% else %}

Source SQL

Source job no longer cached — re-scan to repopulate.

{% endif %} {# Evidence — collapsed by default, sits below the Source SQL so the page leads with what's actionable. Same `
` collapsible pattern audit's job_detail.html uses for Metadata + Referenced Tables. #} {% if row.evidence_snapshot %}

Evidence

{% for key, value in row.evidence_snapshot.items() %}
{{ key }}
{% if value is mapping or (value is iterable and value is not string) %} {{ value | tojson }} {% else %} {{ value }} {% endif %}
{% endfor %}
{% endif %}
{% endblock %}