{# Single-job detail. Layout matches governor_web idioms: - Tiny text-link back nav (NOT a giant pill button) - `ui-stat-card` strip for cost / bytes / slot time / statement type - `ui-pane` for metadata, referenced tables, issues, and SQL - Expandable+copy SQL viewer adapted from packages/web/src/governor_web/templates/components/code_viewer.html. #} {% extends "layout.html" %} {% block content %} {# Macro imports hoisted out of per-section conditionals. Jinja {% from %} scope is the enclosing block: section_title was being imported inside {% if issues %} but also called inside {% if job.performance_insights %} a few sections below, so a job with insights but no detection issues crashed with ``'section_title' is undefined``. #} {% from "components/_stat_card.html" import stat_card %} {% from "components/_section_title.html" import section_title %}
{# Coming from the cohort chart? Offer a focused back-link to the cohort page anchored at this execution. Falls back to the standard dashboard link otherwise. The cohort page renders an `id="execution-{{ job_id }}"` anchor we can scroll to. #} {% if request.query_params.get('from') == 'cohort' and job.destination_table %} ← Back to job history {% endif %} ← Back to dashboard

{{ job.destination_table }}

{{ job.job_id }}

{# Statement type + workload pills moved here so the stat strip below can dedicate all six cards to numeric cost / bytes / slot data (mirrors the opportunity detail page). #}

{{ job.statement_type }} {% if job.workload_kind == "build" %} build {% elif job.workload_kind == "consumption" %} consumption {% endif %} {% if job.is_dbt_originated %} dbt {% else %} other {% endif %} {% if job.cache_hit %} cache hit {% endif %} {# Spec 156 - origin chip mirrors the /opportunities Origin column. ad_hoc renders as muted slate so it doesn't compete with the explicit dbt / build / consumption pills above. #} {% if job.origin_tool and job.origin_tool != 'ad_hoc' %} {{ job.origin_tool | replace('_', ' ') }} {% endif %}

{# Spec 156 - Origin card. Shows the classified origin_tool, the classifier_signal (the rule that fired - audit-of-audit transparency), and any tool-specific keys we extracted (looker_dashboard_id, fivetran_connector_id, etc.). Only rendered when origin_metadata is present; ad_hoc rows still get the card so the user can see "we tried and found no attribution signal". #} {% if job.origin_metadata %}
{{ section_title("Origin") }}
Tool
{{ job.origin_tool or 'ad_hoc' }}
{% for key, value in job.origin_metadata.items() %} {% if key != 'classifier_signal' %}
{{ key | replace('_', ' ') | title }}
{{ value }}
{% endif %} {% endfor %}
Classifier signal
{{ job.origin_metadata.classifier_signal or 'fallback:none_matched' }}
{% endif %} {# ── Six-card stat strip ── aggregated lifetime metrics for the destination table on the left half, last-execution snapshot on the right half. Mirrors opportunities/detail.html so the two surfaces feel consistent. ``grid-cols-6`` isn't in the audit's pre-compiled Tailwind bundle, so the xl breakpoint rule is inlined here; smaller widths fall back to the 1/2/3 column Tailwind utilities. #} {%- set _agg_meta = ("across " ~ cohort_aggregates.execution_count ~ " executions") if (cohort_aggregates and cohort_aggregates.execution_count and cohort_aggregates.execution_count > 1) else None -%}
{% if cohort_aggregates %} {{ stat_card("Cost (aggregated)", cohort_aggregates.cost_usd | format_usd, meta=_agg_meta) }} {{ stat_card("Bytes Processed (aggregated)", cohort_aggregates.bytes_processed | format_bytes, meta=_agg_meta, accent="brand") }} {{ stat_card("Slot Time (aggregated, hours)", "%.1f" | format((cohort_aggregates.slot_time_ms or 0) / 3600000), meta=_agg_meta) }} {% else %} {# Job has no destination_table - fall back to this execution's own metrics in the left trio so the strip still reads sensibly. #} {{ stat_card("Cost", job.cost_usd | format_usd) }} {{ stat_card("Bytes Processed", job.bytes_processed | format_bytes, meta=("billed: " ~ (job.bytes_billed | format_bytes)), accent="brand") }} {{ stat_card("Slot Time (hours)", "%.1f" | format((job.slot_time_ms or 0) / 3600000), meta=("duration: " ~ (job.duration_ms | format_duration))) }} {% endif %} {% if latest_execution %} {%- set _last_when = latest_execution.creation_time.strftime("%Y-%m-%d %H:%M UTC") if latest_execution.creation_time else None -%} {{ stat_card("Last Execution Cost", latest_execution.cost_usd | format_usd, meta=_last_when) }} {% if latest_execution.bytes_processed is not none %} {{ stat_card( "Last Execution Bytes", latest_execution.bytes_processed | format_bytes, meta=("billed: " ~ (latest_execution.bytes_billed | format_bytes)) if latest_execution.bytes_billed is not none else None, accent="brand" ) }} {% else %} {{ stat_card("Last Execution Bytes", "-", accent="slate") }} {% endif %} {% if latest_execution.slot_time_ms is not none %} {{ stat_card( "Last Execution Slot (hours)", "%.1f" | format((latest_execution.slot_time_ms or 0) / 3600000), meta=("duration: " ~ (latest_execution.duration_ms | format_duration)) if latest_execution.duration_ms is not none else None ) }} {% else %} {{ stat_card("Last Execution Slot (hours)", "-", accent="slate") }} {% endif %} {% else %} {# Cohort has no cached executions (shouldn't really happen since THIS job IS one) - render dash placeholders so the grid keeps its 6-card shape rather than visually collapsing. #} {{ stat_card("Last Execution Cost", "-", accent="slate") }} {{ stat_card("Last Execution Bytes", "-", accent="slate") }} {{ stat_card("Last Execution Slot (hours)", "-", accent="slate") }} {% endif %}
{# ── Issues found by detection rules ── #} {% if issues %}
{{ section_title( "Issues Found (" ~ (issues | length) ~ ")", description="Detection rules that flagged this job." ) }}
{% for issue in issues %} {% endfor %}
Rule Detail Affected Table Current Cost
{{ issue.display_name }} {% if issue.explanation %}

{{ issue.explanation }}

{% else %} - {% endif %}
{{ issue.affected_table }}
{{ issue.current_cost | format_usd }}
{% endif %} {# ── Cost trend and issues ── Shared partial: cohort line chart for this job's destination_table. Days where any execution tripped a detection rule render as red dots; days that ran clean stay blue. Empty when the table has no executions in the scan window. #} {% if chart_points_payload %}
{% with chart_points_payload=chart_points_payload, chart_title="Cost trend and issues", chart_subtitle="One point per day across the scan window. Days without executions are skipped. Red dots mark days when this table's executions tripped a detection rule.", chart_history_href="/jobs/" ~ job.destination_table %} {% include "jobs/_cost_chart.html" %} {% endwith %}
{% endif %} {# ── SQL viewer (expandable + copy, same pattern as governor_web/components/code_viewer.html) ── #}
SQL
{{ job.query | length }} chars
{% for line in job.query.split('\n') %}{{ loop.index }}{{ line }}
{% endfor %}
{# ── Performance insights (raw payload, collapsible) ── #} {% if job.performance_insights %}
{{ section_title( "Performance Insights", description="Raw payload from query_info.performance_insights." ) }}
{{ job.performance_insights | tojson(indent=2) }}
{% endif %} {# ── Metadata: open by default. Pushed below Performance Insights so the page leads with what's actionable. The "Referenced Tables" section that used to live alongside has been removed - referenced tables are already discoverable from the SQL itself. ── #}

Metadata

Created
{{ job.creation_time.strftime("%Y-%m-%d %H:%M:%S") }} UTC
{% if job.start_time %}
Started
{{ job.start_time.strftime("%Y-%m-%d %H:%M:%S") }} UTC
{% endif %} {% if job.end_time %}
Finished
{{ job.end_time.strftime("%Y-%m-%d %H:%M:%S") }} UTC
{% endif %}
User
{{ job.user_email }}
Job state
{{ job.job_state }}
Job type
{{ job.job_type }}
{% if job.query_hash %}
Query hash
{{ job.query_hash }}
{% endif %}
{% endblock %}