{# Spec 154 v3 - materialization section rendered on the opportunity detail page. Fires when one or more of this table's opportunities carries a materialization payload - either an audit-detected candidate (``materialization_candidate``, possibly with any of the four strategies below) or Google's MV Recommender pass-through (``bq_materialization_recommendation``). The audit-detected card branches on ``recommended_strategy``: * ``materialized_view`` - full MV card with generated SQL tabs and the stat strip (cost / executions / avg bytes). * ``ephemeral`` - inline-as-CTE card with the dbt node id + the downstream models that would absorb the inline. * ``table`` - persist-as-table card with build vs. read counts. * ``incremental`` - switch-to-incremental card with the representative build SQL so the user can see the filter the fitter matched. Inputs from the route: ``audit_mv_candidates`` - list of evidence_snapshot dicts (one per audit-detected opportunity on this table); each carries a ``recommended_strategy`` field. ``bq_mv_recommendations`` - list of dicts (BQ Recommender rows) each with: target_resource, target_table_full_name, bq_description, bq_recommendation_state, bq_insights[], last_refresh_time Renders nothing when both lists are empty, so the partial can be included unconditionally from detail.html. #} {% macro strategy_pill(strategy) %} {%- if strategy == "ephemeral" -%} ephemeral {%- elif strategy == "incremental" -%} incremental {%- elif strategy == "table" -%} table {%- elif strategy == "materialized_view" -%} materialized view {%- else -%} {{ strategy or "unspecified" }} {%- endif -%} {% endmacro %} {% macro candidate_summary(strategy, source_label, object_label=None, state=None) %}
{{ strategy_pill(strategy) }} {{ source_label }} {% if state %} {{ state }} {% endif %}
{% if object_label %}

{{ object_label }}

{% endif %}
{% endmacro %} {# Concise decision block. The raw evidence payload has four verbose fields; the UI only needs the three decision points a reviewer acts on: why this surfaced, what to do, and what changes if they do it. Caveats stay as one compact "Check" line. #} {% macro reasoning_block(reasoning, caveats=None) %} {% if reasoning %}
{% if reasoning.observation %}

Evidence

{{ reasoning.observation }}

{% endif %} {% if reasoning.recommendation %}

Action

{{ reasoning.recommendation }}

{% endif %} {% if reasoning.expected_benefit %}

Impact

{{ reasoning.expected_benefit }}

{% endif %}
{% endif %} {% if caveats %}

Check

{{ caveats[0] }} {% if caveats | length > 1 %} +{{ caveats | length - 1 }} more {% endif %}

{% endif %} {% endmacro %} {% if audit_mv_candidates or bq_mv_recommendations %} {% from "components/_section_title.html" import section_title %}
{{ section_title( "Materialization", description="Recommended way to persist this object, based on rebuild cost, read patterns, and downstream use.", aside=((audit_mv_candidates | length + bq_mv_recommendations | length) ~ (' candidate' if (audit_mv_candidates | length + bq_mv_recommendations | length) == 1 else ' candidates')) ) }}
{# ─── Audit-detected candidates ────────────────────────────── #} {% for cand in audit_mv_candidates %} {% set _strategy = cand.recommended_strategy or "materialized_view" %} {% if _strategy == "materialized_view" %} {# MV card. Spec 154 v3.3: just the reasoning block; no SQL panel and no stat-strip cards. The numbers live in the observation line; the user looks at this surface to decide whether to act, not to copy SQL. #}
{{ candidate_summary(_strategy, "audit-detected", cand.suggested_mv_name) }} {{ reasoning_block(cand.reasoning, caveats=cand.safety_caveats) }}
{% elif _strategy == "ephemeral" %}
{{ candidate_summary(_strategy, "audit-detected", cand.dbt_node_id) }} {{ reasoning_block(cand.reasoning, caveats=cand.caveats) }} {# Downstream-consumer list is distinct content (node names), not in the observation prose, so it stays. #} {% if cand.downstream_dbt_node_ids %}

Consumers

{% for downstream in cand.downstream_dbt_node_ids[:3] %} {{ downstream }} {% endfor %} {% if cand.downstream_dbt_node_ids | length > 3 %} +{{ cand.downstream_dbt_node_ids | length - 3 }} more {% endif %}
{% endif %}
{% elif _strategy == "table" %}
{{ candidate_summary(_strategy, "audit-detected") }} {{ reasoning_block(cand.reasoning, caveats=cand.caveats) }}
{% elif _strategy == "incremental" %}
{{ candidate_summary(_strategy, "audit-detected", cand.dbt_node_id) }} {{ reasoning_block(cand.reasoning, caveats=cand.caveats) }}
{% else %} {# Unknown strategy - render a minimal fallback so we never silently swallow an opportunity. #}
{{ candidate_summary(_strategy, "audit-detected") }}
{{ cand | tojson(indent=2) }}
{% endif %} {% endfor %} {# ─── BigQuery's MV Recommender pass-through ──────────────── #} {% for rec in bq_mv_recommendations %}
{{ candidate_summary("materialized_view", "BigQuery Recommender", rec.target_table_full_name or rec.target_resource, rec.bq_recommendation_state) }} {% if rec.bq_description %}

{{ rec.bq_description }}

{% endif %} {% if rec.bq_insights %}

Insights

    {% for ins in rec.bq_insights[:2] %}
  • {{ ins.category }} {{ ins.description }}
  • {% endfor %} {% if rec.bq_insights | length > 2 %}
  • +{{ rec.bq_insights | length - 2 }} more insights
  • {% endif %}
{% endif %} {% if rec.last_refresh_time %}

Last refreshed by Google's Recommender: {{ rec.last_refresh_time }}

{% endif %}
{% endfor %}
{% endif %}