{# Run-detail body (issue #160). Every form posts to the existing /runs/{id}/... action endpoints. The page heading and primary navigation (Audio / Transcript / Review) live in the wrapper templates' command-bar blocks, not in this partial. #}

Summary

Status
{{ run.status }} {% if run_archived %}archived{% endif %}
Current stage
{{ humanize_stage(run.current_stage) if run.current_stage else "Not started" }}
{% if run.error %} {% set nerr = normalize_error(run.error) %}

{{ nerr.label if nerr else run.error }}

{% if nerr and nerr.hint %}

{{ nerr.hint }}

{% endif %} {% if nerr and nerr.raw != nerr.label %}
Raw error
{{ nerr.raw }}
{% endif %} {% endif %} {% if run.status == "failed" %}
{# Carry the revision we rendered so a stale tab's requeue 409s (exact-revision CAS). #} Retries the failed stage ({{ run.current_stage or "not started" }}).
{% if run.current_stage in ("transcribe", "diarize_embed") %} {# Static guidance only; no live probe on this page (a health check here would add model-service latency to every failed-run view and flap with service health; the setup wizard's SERVICES step owns live reachability). #}

This stage needs the model services (ASR / diarizer / embedder). If they aren't running, start a compute tier: docker compose -f compose.yaml -f compose.gpu.yaml up -d (NVIDIA GPU) or docker compose -f compose.yaml -f compose.cpu.yaml up -d (no GPU), wait for them to come up, then requeue. The shipped models run offline; an HF_TOKEN is only needed if you overrode diarization to a gated pipeline.

{% endif %} {% endif %} {% if run.status in ("queued", "running") %}
{% if run.status == "running" %} Holds the run after the current stage ({{ run.current_stage or "not started" }}) finishes. The stage now running completes first, then no further stages start until you resume. {% else %} Holds this run in the queue. It will not start processing until you resume it. {% endif %}
{% endif %} {% if run.status in ("queued", "running", "awaiting_adjudication") %}
{% if run.status == "running" %} Stops the run. The stage now running ({{ run.current_stage or "not started" }}) finishes first; cancel is cooperative, not an immediate kill. No further stages start. Media and any partial results are left in place (delete is separate). {% else %} Stops the run; no further stages will run. Media is left in place (delete is separate). {% endif %}
{% endif %} {% if run.status == "paused" %}
Resumes processing from where it was paused ({% if run.current_stage %}{{ run.current_stage }}{% else %}the beginning{% endif %}).
Cancels this paused run. Media is left in place.
{% endif %}
{# Gate on the live status too: a bookmarked/refreshed ?enqueue=deferred URL must not keep asserting "is queued" after the sweep already republished it. #} {% if enqueue_deferred and run.status == "queued" %}

The initial enqueue was deferred because the task broker was unavailable. This run is queued and will start automatically once the broker recovers; no action needed.

{% endif %} {% if media_delete_result %}

Deleted {{ media_delete_result.files }} derived audio file{{ "" if media_delete_result.files == 1 else "s" }}{% if media_delete_result.missing %} ({{ media_delete_result.missing }} already gone){% endif %}{% if media_delete_result.failed %}, {{ media_delete_result.failed }} could not be removed and will be swept later{% endif %}. The original source recording was kept.

{% endif %} {% if run_archived %}

This run is archived, hidden from the runs list and the review queue. All its data is intact; un-archive to restore it.

{% endif %} {% if run_terminal %} {# Archive / un-archive + derived-media deletion (issue #5). Only terminal runs (completed / failed / cancelled); a live run must be cancelled first. #}

Manage

{% if not run_archived %} {% if restart_impact and restart_impact.has_blockers %}
Blocked: this run has {{ restart_impact.segment_scope_decisions }} segment-scope ruling(s) and {{ restart_impact.enrichment_evidence }} enrichment evidence row(s) that reference specific transcript segments and cannot be deleted. Submit the file as a new run instead.
{% elif restart_impact and restart_impact.label_scope_decisions > 0 %}
Queues the run to re-process all stages from the beginning. Prior stage results are kept in the ledger as earlier attempts.
{% else %}
Queues the run to re-process all stages from the beginning. Prior stage results are kept in the ledger as earlier attempts.
{% endif %} {% endif %} {% if run_archived %}
Restores this run to the runs list and review queue.
{% else %}
Hides this run from the runs list and review queue. Keeps all its data; reversible.
{% endif %}
Frees disk by removing this run's preprocessed and chunked audio. Irreversible; the original source recording is kept.
{% endif %} {% if source_metadata %} {# Scraped acquisition context (issue #36), write-once and shown apart from the operator's own notes so human and scraped input are never conflated. The URLs here are sanitized allowlist values (absolute http(s), no userinfo), unlike the raw source_url, which never reaches a template. #}

Source metadata

{% if source_metadata.title %}
Title
{{ source_metadata.title }}
{% endif %} {% if source_metadata.uploader or source_metadata.channel %}
Uploader
{{ source_metadata.uploader or source_metadata.channel }} {% if source_metadata.channel and source_metadata.uploader and source_metadata.channel != source_metadata.uploader %} · {{ source_metadata.channel }} {% endif %} {% if source_metadata.channel_url %} · channel {% endif %}
{% endif %} {% if source_metadata.upload_date %}
Uploaded
{{ source_metadata.upload_date.isoformat() }}
{% endif %} {% if source_metadata.duration_seconds %}
Claimed duration
{{ "%d:%02d" | format(source_metadata.duration_seconds // 60, source_metadata.duration_seconds % 60) }} (source-reported, not measured)
{% endif %} {% if source_metadata.tags %}
Tags
{{ source_metadata.tags | join(", ") }}
{% endif %} {% if source_metadata.canonical_url %}
Page
{{ source_metadata.canonical_url }}
{% endif %} {% if source_metadata.extractor %}
Extractor
{{ source_metadata.extractor }}{% if source_metadata.extractor_version %} (yt-dlp {{ source_metadata.extractor_version }}){% endif %}
{% endif %}
{% if source_metadata.description %}
Description

{{ source_metadata.description }}

{% endif %}
{% endif %}

Operator notes

{% include "legacy_runs/run_assets.html" %} {% include "legacy_runs/run_translation.html" %} {% if speaker_timeline is not none %}

Speaker timeline

{% endif %}
Technical details

Source

{{ run.media_item.source_path }}

{% if provenance %} {# URL runs only: the acquisition source, shown as a bare host; never the raw source_url, whose query could carry a signed token. #}

Source: {{ provenance }}

{% endif %}
Revision
{{ run.revision }}
Created
{{ run.created_at.strftime("%Y-%m-%d %H:%M:%S") }}
Updated
{{ run.updated_at.strftime("%Y-%m-%d %H:%M:%S") }}

Complete run archive (JSON) {% if transcript_available %} · transcript & subtitle exports are on the Transcript page{% endif %} {% if media_reclaimed_at %} · Media reclaimed on {{ media_reclaimed_at.strftime("%Y-%m-%d") }} (transcript and decisions kept; re-run from source to restore audio). {% elif not audio_available and not transcript_available %} · No audio or transcript yet. {% endif %}

Pipeline models

The model each stage saw just before it ran. This is observed immediately before the attempt, not read back from the output, so it records which model answered rather than proving the exact build.

{% for stage in model_provenance %}

{{ stage.label }}{% if stage.recorded and stage.attempt and stage.attempt > 1 %} (from attempt {{ stage.attempt }}){% endif %}

{% if not stage.recorded %}

Not recorded. This run finished before model provenance was added, no attempt of this stage completed, or the latest completed attempt did not record it.

{% else %}
{% for role in stage.roles %}
{{ role.label }}
{% if role.reachable %}
{% if role.model %}{{ role.model }}{% else %}no model name reported{% endif %} {% if role.engine %} engine {{ role.engine }}{% endif %} {% if role.revision %}
revision {{ role.revision }}{% endif %} {% if role.decode_config_hash %}
decode config {{ role.decode_config_hash[:12] }}{% endif %}
{% else %}
Not observed{% if role.detail %} ({{ role.detail }} when the stage ran){% endif %}.
{% endif %} {% endfor %}
{% endif %}
{% endfor %}
{# Glossary provenance (issue #123): the exact vocabulary hint whisper decoded this run with, the operator's glossary unioned with the pack's words. Read-only; NULL means the run had no vocabulary or predates this record. #}

Glossary applied

The vocabulary hint transcription saw for this run, the terms from your glossary and the run's domain pack. It records what the model was told to expect, not that every term was spelled this way.

{% if run.initial_prompt %}

{{ run.initial_prompt }}

{% else %}

Not recorded. This run has not been transcribed yet, had no vocabulary, or it finished before Voxint began recording the applied glossary.

{% endif %}
{# Detected language (issue #124): what whisper decided when it transcribed this run. The score is whisper's own detection probability, shown with honest framing: it is not a calibrated confidence and says nothing about a recording that switches languages mid-way. #}

Detected language

{% if run.detected_language %}

{{ language_label(run.detected_language) }}

{% if run.detected_language_probability is not none %}

Language detection confidence: {{ confidence_band(run.detected_language_probability) }}. This is the model's own assessment of its language guess, not a measure of transcript accuracy. It reflects the whole recording as one language; a recording that mixes languages still gets a single detected language. Short or mostly silent recordings can produce low or arbitrary detections.

{% else %}

No detection score was recorded for this run.

{% endif %} {% else %}

Not recorded. This run has not been transcribed yet, or it finished before Voxint began recording the detected language.

{% endif %}

Stage ledger

{% if not stage_runs %}

No stage attempts recorded yet.

{% else %}
STAGEATTEMPTSTATUSWORKER STARTEDFINISHEDLEASE EXPIRESERROR
{% for s in stage_runs %}
{{ s.stage }} {{ s.attempt }} {{ s.status }} {{ s.worker_id or "-" }} {{ s.started_at.strftime("%Y-%m-%d %H:%M:%S") if s.started_at else "-" }} {{ s.finished_at.strftime("%Y-%m-%d %H:%M:%S") if s.finished_at else "-" }} {{ s.lease_expires_at.strftime("%Y-%m-%d %H:%M:%S") if s.lease_expires_at else "-" }} {% if s.error %}{{ s.error }}{% else %}-{% endif %}
{% endfor %}
{% endif %}
{#- Plugin run-detail panels (issue #138): each active plugin's fragments, in (slot, order) order. Empty registry ⇒ no iterations ⇒ zero bytes. -#} {%- for panel in plugin_run_detail_panels %} {% include panel.template %} {%- endfor %}