{% extends "base.html" %} {% from "_recon_table.html" import fmt, render_table %} {% block title %}Loan forensic — RWA Calculator{% endblock %} {% block content %} {# The breadcrumb leads with where the analyst CAME FROM (a template cell, when they arrived from one) — the report root and the explorer are always-available fallbacks, not the trail back. #}

{% if return_to and return_to != explorer_url %}← Back to the cell you came from · {% endif %}the report · per-key explorer

{% if not detail %} {# The key resolved to no single reconciled row. Never a bare 404: the loan very probably exists and was addressed by ONE of the join key's columns. #}

That key does not address one reconciled row

{{ resolution.requested }}

{{ resolution.reason }}
{% if resolution.key_columns %}

This reconciliation's join key is {{ resolution.key_columns|join(" + ") }}.

{% endif %} {% if resolution.candidates %}

Did you mean

{% endif %}

Open the per-key explorer

{% else %}

Loan forensic

{{ detail.recon_key }}

{% if signoff_error %}
{{ signoff_error }}
{% endif %}
{{ detail.row_bucket or "—" }}
row bucket
{{ detail.worst_component or "—" }}
worst component
{{ detail.exposure_class or "—" }}
exposure class
{{ detail.approach or "—" }}
approach

By component — RWA driver chain

The loan's RWA build, legacy vs ours at each step. Indented rows are our-side drivers explaining how we reached the value above; a driver tagged legacy not provided has no legacy counterpart in the mapping.

{% for step in detail.steps %} {% for d in step.drivers %} {% if d.legacy_available %} {% else %} {% endif %} {% endfor %} {% endfor %}
steplegacyoursabs Δrel Δstatus
{{ step.label }} {{ fmt(step.legacy) }} {{ fmt(step.ours) }} {{ fmt(step.abs_delta) }} {{ fmt(step.rel_delta) }} {{ step.bucket if step.bucket is not none else "" }}
↳ {{ d.name }}{{ fmt(d.legacy) }}legacy not provided{{ fmt(d.ours) }}
{% if detail.breaks.rows %}

Breaks for this key

{{ render_table(detail.breaks) }} {% endif %} {% if placement %}

Where this exposure lands

{% if not placement.available %}
{{ placement.reason }}
{# The one click that resolves the common case. The compare page generates both sides once and memoises them, so coming back fills this panel in; where the comparison cannot be built at all, that page repeats the same reason with the mapping remedy beside it, so the link is never a dead end. #}

Open the return-template comparison

{% elif not placement.groups %}
This exposure reached no row of any instrumented return template. That is not the same as reporting nothing — only C 07.00, C 08.01 and C 08.03 carry cell membership, so an exposure outside them has no addressable placement here.
{% else %}

Which template row this exposure reported in, ours beside theirs. A row only one side reaches is a placement difference, and says so — it is never left blank. Rows are grouped by the population that backs them: a template row does not have one population, so the same row can appear under more than one group.

{% for group in placement.groups %}

{{ group.template_label }}{% if group.sheet %} · {{ group.sheet }}{% endif %} — population {{ group.predicate_key }}{% if group.columns %}, serving columns {{ group.columns|join(", ") }}{% endif %}

{% if group.moved %}
Moved row: {{ group.our_placement }} {{ group.our_placement_name }} (ours) → {{ group.their_placement }} {{ group.their_placement_name }} (theirs).
{% else %}

Placement — ours: {{ group.our_placement_name }} {%- if group.our_placement %} ({{ group.our_placement }}){% endif %}; theirs: {{ group.their_placement_name }} {%- if group.their_placement %} ({{ group.their_placement }}){% endif %}.

{% endif %}
{# The hierarchy claim is PER SIDE. One collapsed column taking ours and falling back to theirs rendered a row that is a provable leaf for us and indistinguishable for them as a flat "leaf", with a note asserting it of both. That is the tri-state surviving every dataclass and dying at the last step, so the two states are columns, never one. #} {% for row in group.rows %} {# A side that did not reach the row is DIMMED, never emptied — the word is the finding. ``muted`` is app.css's own class; the ``is-*`` state names used on the compare grid are inline-styled there, not CSS rules, so borrowing them here would style nothing. #} {{ row.our_display }} {{ row.their_display }} {{ row.our_parent_state }} {{ row.their_parent_state }} {% endfor %}
rowrow nameourstheirs hierarchy (ours)hierarchy (theirs)
{{ row.row_ref }} {{ row.row_name or "—" }}
{% endfor %} {% endif %} {% endif %}

Sign-off

Record whether this exposure's difference is acceptable. Accepting or rejecting takes it off the Open worklist; reopen it to put it back.

{% if decision and signoff_stale %}
⚠ This exposure was {{ decision.status }} on {{ decision.decided_at }}{% if decision.reason %} (“{{ decision.reason }}”){% endif %}, but the difference has changed since — please re-review before signing off again.
{% elif decision %}
This exposure is {{ decision.status }}{% if decision.decided_at %} · decided {{ decision.decided_at }}{% endif %}. {% if decision.reason %}
Reason: {{ decision.reason }}
{% endif %}
{% endif %}

A reason is required to reject; optional to accept.

{% if decision %}{% endif %}
{% endif %} {% endblock %}