{#- Pipeline progress strip (#423), htmx-polled partial fragment. The outermost div carries hx-get + hx-swap="outerHTML" so each poll replaces the whole strip (including its own hx-trigger, which the server adapts between 15s active and 60s idle). The page-level timer script in runs.html ticks [data-started-at] elements every second — no script lives inside this fragment. -#}
{% if dashboard.is_idle %}
Pipeline idle{% if dashboard.workload.queue_paused %} · queue paused{% endif %}{% for s in dashboard.stages if s.degraded %} · {{ humanize_stage(s.stage) }} paused: {{ s.degraded_reason }}{% endfor %}
{% else %}
{% for s in dashboard.stages %}
{{ humanize_stage(s.stage) }}
{{ s.active }} {% if s.queued > 0 %}+{{ s.queued }}q{% endif %}
{% if s.degraded %}
paused: {{ s.degraded_reason }}
{% endif %} {% if s.active > 0 and s.active_started_at %}
{{ format_compact_duration((dashboard.generated_at - s.active_started_at).total_seconds()|int) }}
{% endif %} {% if s.active > 0 and s.eta_seconds is not none %}
~{{ format_compact_duration(s.eta_seconds|int) }} left
{% endif %}
{% endfor %}
{{ dashboard.workload.outstanding }} outstanding {% if dashboard.workload.elapsed_seconds is not none %} · {{ format_compact_duration(dashboard.workload.elapsed_seconds|int) }} elapsed {% endif %} {% if dashboard.workload.overrun %} · taking longer than usual {% elif dashboard.workload.drain_eta_seconds is not none %} · ~{{ format_compact_duration(dashboard.workload.drain_eta_seconds|int) }} remaining {% endif %} {% if dashboard.workload.queue_paused %} · queue paused {% endif %}
{% endif %}