{# Vendored from opportunities/detail.html. Same shell, stat strip, issue cards, and improvements catalogue. Spec 149 deltas: - breadcrumb back to /failures - eyebrow "Failure detail" (vs "Recommendation") - "Errors observed" section heading (vs "Issues found") - "Slot Wasted" stat replaces "Cost (this execution)" (failures are billed $0) - new "Failed query" code block before the improvements section so the broken SQL is always visible. #} {% extends "layout.html" %} {% block content %} {# Hoisted from per-section conditionals - see opportunities/detail.html for the same fix. Jinja {% from %} scope is the enclosing block, so when ``issue_cards`` is empty (suggestion-only failure) the section_title import never fires but the Available improvements block at the bottom still calls section_title and crashes. #} {% from "components/_stat_card.html" import stat_card %} {% from "components/_section_title.html" import section_title %} {% from "components/_sql_panel.html" import sql_panel %}
Failures {% if row.dbt_model_name %}{{ row.dbt_model_name }}{% else %}{{ row.affected_object or row.affected_table }}{% endif %}
{# Title includes the dataset prefix so readers can locate the failed object without scanning a subtitle. The full project.dataset.table path lives in affected_table; the project context is in the global header strip. #} {% set _path_parts = (row.affected_table or '').split('.') %} {% set _dataset = _path_parts[1] if _path_parts | length >= 2 else '' %} {% set _table = _path_parts[2] if _path_parts | length >= 3 else (row.affected_object or row.affected_table or '') %}

{% if _dataset %}{{ _dataset }}.{% endif %}{{ row.dbt_model_name or _table }}

{# Title-row chips carry only context that isn't restated below (dbt origin, author filter-pivot). Error-category + suggestion counts removed - they appear in the stat strip and in the section headings below. #}
{% if row.dbt_model_name %} dbt {% endif %} {% if row.author_email %} {{ row.author_email }} {% endif %}
{# Stat strip - three operational numbers for the failed query group: Failed executions / Bytes / Slot wasted. The previous fourth card listed every error-category rule name as plain text, duplicating the "Errors observed" section directly below. Dropped to three; each remaining card carries more weight. #}
{{ stat_card("Failed executions", (visible_evidence.executions if visible_evidence else "-"), accent="rose", padding="p-6") }} {% if job and job.total_bytes_processed is not none %} {{ stat_card( "Bytes Processed", job.total_bytes_processed | format_bytes, meta=("billed: " ~ (job.total_bytes_billed | format_bytes)) if job.total_bytes_billed is not none else None, accent="brand", padding="p-6" ) }} {% else %} {{ stat_card("Bytes Processed", "-", accent="slate", padding="p-6") }} {% endif %} {% if visible_evidence and visible_evidence.slot_wasted %} {{ stat_card("Slot Wasted", visible_evidence.slot_wasted, meta="summed across every failed execution", accent="rose", padding="p-6") }} {% else %} {{ stat_card("Slot Wasted", "-", accent="slate", padding="p-6") }} {% endif %}
{# Cost trend collapsible. "Cost trend" (not "for this table") because the same partial is used for consumption queries that have no destination table. History link is rendered inside the partial via ``chart_history_href``. #} {% if chart_points_payload %} {% set chart_title = "Cost trend" %} {% set chart_subtitle = "One point per day across the scan window. Days without executions are skipped." %} {% set chart_collapsible = true %} {% set chart_history_href = "/jobs/" ~ row.affected_table %} {% include "jobs/_cost_chart.html" %} {% endif %} {# Issues found - one bordered card per issue rule that fired on this table. Each card has a 2-column Why-this-fired / Recommended- fix split, so readers can pair the cause with the fix per rule. When a rule has no template SQL diff, Recommended-fix points to the Available improvements section below rather than repeating the Why-this-fired text verbatim. When zero issues fired (suggestion-only object), this section is hidden. #} {% if issue_cards %}
{{ section_title( "Errors observed", description="Distinct BigQuery error reasons across this group's executions. The reason code links back to Google's docs; the message is the verbatim runtime error from the most-recent failure.", aside=((issue_cards | length) ~ (' reason' if issue_cards | length == 1 else ' reasons')) ) }}
{% for issue in issue_cards %}
{{ issue.row.opportunity_type }}
{# Each column gets its own subtle sub-card so "BigQuery error" and "Try this fix" are visually distinct blocks instead of running together inside the outer card. #}

BigQuery error

{{ issue.row.explanation }}

{% if issue.rule_meta and issue.rule_meta.description %}

{{ issue.rule_meta.description }}

{% endif %}

Try this fix

{% if improvements %}

See Available improvements below - audit found AST rewrites that may be the underlying fix.

{% else %}

No deterministic AST rewrite is available for this query. Inspect the failed SQL below and the BigQuery console for the failed job ID.

{% endif %}
{% endfor %}
{% endif %} {# Failed query block removed per user feedback - the broken SQL is already visible in the Original SQL tab of the Available improvements cards below. #} {# Available improvements - every SQL-rewrite suggestion that fired on any opportunity for this table, deduped by rule_type. Each is a deterministic rewrite (no LLM) and renders inline as its own green/red diff. #} {% if improvements %}
{{ section_title( "Available improvements", description="Deterministic SQL rewrites that fire on the failed query body. Each card carries a green/red diff plus a rollback path.", aside=((improvements | length) ~ (' suggestion' if improvements | length == 1 else ' suggestions')) ) }}
{% for imp in improvements %}
{{ imp.display_name }} {% if imp.template_id %} deterministic rewrite {% endif %} {% if imp.occurrences and imp.occurrences > 1 %} ×{{ imp.occurrences }} {% endif %}
{% if imp.explanation %}

{{ imp.explanation }}

{% endif %} {% if imp.diff_lines or imp.before_content %}
{% if imp.after_content %} {% endif %}
{% if imp.diff_lines %} {% set diff_lines = imp.diff_lines %} {% set file_path = imp.template_id %} {% include "components/code_diff.html" %} {% else %}

No diff available - template did not produce a change.

{% endif %}
{% if imp.after_content %} {% endif %}
{% endif %} {% if imp.rollback %}
Rollback instructions

{{ imp.rollback }}

{% endif %}
{% endfor %}
{% endif %} {# Evidence + BigQuery query lookup - two side-by-side cards on >=lg, stacked on smaller screens. Each card collapses independently. The right-hand card renders the ``INFORMATION_SCHEMA.JOBS_BY_PROJECT`` SELECT pre-filtered by job_id so the user can paste it into the BigQuery console and inspect the row themselves. Evidence + lookup come from the representative opportunity (highest-priority for this table). #} {% if visible_evidence or job_lookup_sql %}
{% if visible_evidence %}

Evidence

{% for key, value in visible_evidence.items() %}
{{ key }}
{% if value is mapping or (value is iterable and value is not string) %} {{ value | tojson }} {% else %} {{ value }} {% endif %}
{% endfor %}
{% endif %} {% if job_lookup_sql %} {{ sql_panel("job-lookup-sql", "BigQuery query", job_lookup_sql, caption="Paste this into the BigQuery console to inspect the underlying job row.") }} {% endif %}
{% endif %}
{# Generic tab toggler - picks up every ``[data-tab]`` / ``[data-tab-pane]`` pair inside an ancestor with ``[data-active-tab]``. Same script as the cohort page so suggestion cards' Diff / Original SQL tabs work without coupling to the class name. #} {% endblock %}