{# The label list — full-page include and htmx post-decision refresh target. #} {% macro hint_state_pill(hint) %} {% if hint.state.value == "accepted" %}accepted {% elif hint.state.value == "rejected" %}rejected {% elif hint.state.value == "superseded" %}superseded {% endif %} {% endmacro %} {# Review-priority + explainable breakdown (issue #42). Raw facts, never a "% confidence": priority is an attention-ordering signal, not a probability. #} {% macro triage_badge(hint) %} {% set t = name_triage.get(hint.candidate.id) %} {% if t %} 1 %} · {{ t.components.peer_producer_count|int }} producers agree{% endif %}">priority {{ "%.2f"|format(t.priority) }} {% if t.components.voice_conflict %}voice conflict{% endif %} {% if t.unresolved_peers %}+{{ t.unresolved_peers }} more proposed{% endif %} {% endif %} {% endmacro %} {% macro hint_decision_forms(hint) %} {% if token and hint.state.value == "proposed" %}
{% endif %} {% endmacro %} {# Name suggestions (issue #38): read/heard names are drafts about identity, never identity — accepting records the review, it assigns nothing. #} {% if names_enabled %}

Name hints

{% if name_hints_run %}

Names probably in this recording, mined from its metadata and transcript. Accepting records your review — it does not assign a speaker.

{% elif names_last_run %}

No name suggestions found in this recording's metadata or transcript.

{% else %}

No name sweep has run yet.

{% endif %} {% if token %}
{% endif %}
{% endif %} {# Inline speaker merge (issue #54): the over-split fix, in the review context. Tick the labels that are one person in THIS recording, choose who they are, preview the exact server-computed change, then confirm. Run-local: it records an assign ruling per label; it never merges roster identities (that stays on /speakers). The whole panel lives INSIDE #labels (plain htmx forms, no React root to leak), so every ruling swap re-renders it fresh. #} {% if token and states|length >= 2 %}

Same speaker across labels?

Diarization sometimes splits one person across labels. Tick the labels that are the same speaker in this recording, choose who they are, then preview the exact change before applying.

Same speaker: {% for s in states %} {% endfor %}
{% endif %} {% for s in states %}

{{ s.label }} {% if s.resolution.value == "unresolved" %} needs ruling {% elif s.resolution.value == "grounded_cosine" %} machine: {{ s.speaker_name }} {% elif s.resolution.value == "human_assign" %} assigned: {{ s.speaker_name }} {% elif s.resolution.value == "human_exclude" %} excluded {% else %} unknown {% endif %}

{{ s.turn_count }} turns, {{ "%.0f"|format(s.total_seconds) }}s. {# Plain-language voice match (issue #117). "Strong" is conditional on the grounded flag: an ungrounded candidate must never claim strong evidence. #} {% if s.cosine_speaker_name %} {% if s.cosine_grounded %}Strong voice match: {{ s.cosine_speaker_name }}. {% else %}Possible voice match: {{ s.cosine_speaker_name }}.{% endif %} {% endif %} {% if s.llm_hint_name %} Heard name (unverified): “{{ s.llm_hint_name }}”. {% endif %}

{% if s.cosine_speaker_name %} {# The raw similarity and the grounded flag are evidence, not the headline: tuck them behind a closed disclosure so the card leads with the plain match (issue #117). Rendered as a sibling block —
is flow content and cannot nest inside the

above. Only shown when cosine evidence exists. #}

Why this match?

Voice similarity {{ "%.2f"|format(s.cosine_confidence or 0) }} to {{ s.cosine_speaker_name }}{% if s.cosine_grounded %}, strong enough to trust on its own{% else %}, not strong enough to confirm without your check{% endif %}.

{% endif %} {# "Preview this speaker" (issue #49): seeks a CLEAN diarization turn for this label (longest non-overlap, fallback longest), NOT the longest transcript segment — a transcript segment may contain other voices. Rendered disabled; the workbench-player island enables it iff seeking is safe (issue #55). type="button" so it never submits an adjudication form. #} {% set rep = representative_turns.get(s.label) %} {% if rep %}

{% endif %} {% if names_enabled and name_hints_labels.get(s.label) %} {% endif %} {% for seg in previews.get(s.label, []) %}
{# Play just this ASR line's span (issue #49). Server-rendered disabled; the island enables it when seeking is safe (issue #55). type="button". #} {{ "%.1f"|format(seg.start_seconds) }}s {{ seg.enhanced_text or seg.raw_text }} {# Two-scope relabel (issue #54 Phase B): the per-label controls above assign ALL of a label; this assigns just THIS segment. An override shows its speaker + a reset (inherit) that returns the segment to its label live. #} {% if token %} {% set ov = segment_overrides.get(seg.id) %} {% if ov %} → {{ ov.speaker_name }} (this segment)
{% else %}
{% endif %} {% endif %}
{% endfor %} {% if token %} {# Whole-label ruling controls, grouped as one action row (issue #92). #}
{% endif %}
{% else %}

This run has no diarization labels.

{% endfor %}