{# The live activity pane (spec §4.9). Everything is derived from the session NDJSON stream and tagged telemetry; live/ is ephemeral + gitignored, never part of the gate. No timeline → honest idle / expired state (never a faked or blank pane). #} {% import "macros.html" as m %} {% if not live.timeline %} {{ m.empty_inline(live.title, live.reason) }}

live/ is telemetry — ephemeral, gitignored, and never part of the freshness gate (D-LIVE-1: follow-only).

{% else %}
{{ 'ended' if live.ended else ('live now' if live.active else 'last session') }} {{ live.op or 'session' }}{% if live.task %} · {{ live.task }}{% endif %} last event {{ live.age }} ago seq {{ live.last_event.seq }} {{ live.event_count }} event{{ '' if live.event_count == 1 else 's' }}
{% if live.ended %}
{{ '✓' if live.result == 'ok' else '!' }} ended {{ live.result }} in {{ live.duration_s }}s{% if live.run_record %} · run-record {{ live.run_record }} (gated){% endif %}
{% endif %} {# current step + live progress (both derived from the stream) #}
{{ live.step or 'no named step reported' }}
{% if live.progress is not none %}
{{ live.progress }}% — {{ m.derived() }} from progress telemetry
{% endif %} {% if live.latest_log %}
{{ live.latest_log.level }} {{ live.latest_log.msg }}
{% endif %}
session timeline · {{ m.derived() }} telemetry
{% for e in live.timeline %}
{{ '%03d'|format(e.seq) }} {{ e.clock }} {{ e.kind }} {{ e.text }}
{% endfor %}
{% if live.active %} Refresh following live/CURRENT {% else %} Back to current session {{ live.session }} {% endif %} follow-only · refresh to advance the stream
{% endif %}