{# Vendored from opportunities/detail.html. Same shell, stat strip, issue cards, and improvements catalogue. Spec 149 deltas: - back_link to /failures (via the _page_header macro) - eyebrow "Failure detail" (vs "Recommendation") - "Errors observed" section heading (vs "Issues found") - "Slot Wasted" stat replaces "Cost (this execution)" (failures are billed $0). #} {% 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 %} {% from "components/_signal_chip.html" import signal_chip %} {% from "components/_page_header.html" import back_link, header_card %}
{{ back_link("/failures", "Failures") }} {# 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 '') %} {% set _title %}{% if _dataset %}{{ _dataset }}.{% endif %}{{ row.dbt_model_name or _table }}{% endset %} {% call header_card(_title) %} {# Eyebrow parallels opportunities/detail. 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. #} Failure detail {% if row.dbt_model_name %} {{ signal_chip("dbt", color="emerald", title="dbt-originated") }} {% endif %} {% if row.author_email %} {{ row.author_email }} {% endif %} {% endcall %} {# 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. #} {% if chart_points_payload %} {% set chart_title = "Cost trend and issues" %} {# Subtitle is derived from the payload granularity inside the partial (per-execution vs daily roll-up), so we don't hard-code "per day". #} {% set chart_collapsible = true %} {% include "jobs/_cost_chart.html" %} {% endif %} {# Errors observed - one nested sub-card per error reason that fired on this table. Each card has a 2-column BigQuery-error / Try-this-fix split, so readers can pair the cause with the fix per reason. When a reason has no template SQL diff, Try-this-fix points to the Available improvements section below rather than repeating the error text verbatim. When zero reasons 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 %}
{{ signal_chip(issue.row.opportunity_type, color="rose") }}
{# Each column reads as a distinct block - "BigQuery error" and "Try this fix" - inside the one nested sub-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 %}
{# Only show explanation prose when no diff renders below. See opportunities/detail.html for the same rationale (the diff tab already shows what changed). #} {% if imp.explanation and not (imp.diff_lines or imp.before_content) %}

{{ 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]``. Shared with opportunity detail so suggestion cards' Diff / Original SQL tabs work without coupling to the class name. #} {% endblock %}