{{ run.workflow }}run {{ run.id[:12] }}
{# Copy-report button — copies the full report text (the
contents) to the clipboard. Lives in the H1 so it
sits near the run identity, where users naturally look when
they want to share/save a report. Disabled gracefully when
navigator.clipboard is unavailable. #}
{{ run.status }}
{% if run.exit_code is not none %}
exit code {{ run.exit_code }}
{% endif %}
{% if run.started_at %}
started {{ run.started_at }}
{% endif %}
{% if run.path %}
scope {{ run.path }}
{% endif %}
{# data-exclude-run-id filters this strip down to OTHER runs of the
same workflow. Without it, the current run would appear as one
of its own "Recent" siblings (P1-5 in the 2026-05-14 QA punch list). #}
{# Phase 5 — structured recommendation cards. Workflows emit
ATTUNE_REC stdout lines; the runner parses, validates,
and broadcasts them on the recommendation SSE channel.
run_view.js renders each payload as an action card here. #}
{# Phase 3.3 — live progress for discovery-sweep. Hidden unless the
workflow name matches; run_view.js populates the per-source state
from ATTUNE_DS lines on the SSE stream. The static markup orders
sources alphabetically so the layout stays stable; the JS only
flips status without moving DOM nodes. #}
{% if run.workflow == "discovery-sweep" %}
Sources
{% for src in ["bug-predict", "dependency-check", "doc-audit", "pattern-scan", "perf-audit", "security-audit", "test-audit"] %}
⌛{{ src }}
{% endfor %}
{% endif %}
{# T6 (workflow-result-formatting) — structured report panel. Hidden
until run_view.js fetches /runs//report (on page load for
terminal runs, on the SSE done event for live ones) and converts
the renderer's markdown subset to HTML. Runs without a structured
report never reveal this section. #}
{# When stream_url is empty, this run was loaded from disk (older than
the runner's in-memory ring buffer). Server pre-fills the log here
so the page renders without SSE — run_view.js detects the empty
stream_url and skips EventSource creation.
The wrapper stays open while the run streams; once the
structured report panel renders, run_view.js collapses it so the
report is the page's primary content and the raw output becomes a
click-to-expand process log (T6). #}
Process log
{% for line in server_rendered_lines %}{{ line }}
{% endfor %}
{# Phase 3a — collapsible raw stderr from the underlying claude CLI
subprocess when an SDK-backed workflow fails. Default-collapsed so
the page stays scannable; one click expands the redacted stderr.
Block is omitted entirely when sdk_stderr is None (no SDK failure
was captured, or the run predates the Phase 3a schema). See
docs/specs/sdk-error-message-fidelity/. #}
{% if run.sdk_stderr %}
Raw stderr from claude CLI{% if run.sdk_error_kind %} ({{ run.sdk_error_kind }}){% endif %}
{{ run.sdk_stderr }}
{% endif %}
{% if stream_url %}
This page reconnects to the run's stream automatically — refresh anytime
to see the latest output. Buffered output replays for new subscribers,
so you won't lose history.
{% else %}
This run was loaded from disk; the log shown above is its final
captured output. Live streaming is not attached because the run is
no longer in the runner's in-memory ring buffer.
{% endif %}
{% endblock %}
{% block scripts %}
{# Server-injected configuration for run_view.js. Using a tagged
JSON block avoids global-namespace pollution and is unambiguously
data (not a script). #}
{# runner.js provides the shared setupRecentRuns helper that
run_view.js calls. Loaded BEFORE run_view.js so the helper is
available when run_view.js initializes. #}
{% endblock %}