# Phase 14b — Visual Canvas Replay

**Goal:** Render the flow canvas in read-only "replay" mode inside the audit detail panel,
with each node overlaid by its execution status badge, duration, and error tooltip.
Clicking a node on the canvas shows the same node detail as the timeline tab.

---

## Context

Phase 14 delivered a tab-free slide panel with a linear node timeline.
Phase 14b adds a **Canvas tab** alongside the timeline so operators can see
_where_ in the visual graph each failure happened — not just the execution order.

The canvas already renders via the `FlowCanvas` jsPlumb hook in the flow builder.
This phase adds a read-only sibling hook `FlowAuditCanvas` that receives the same
`{nodes, connections}` payload plus a `trace_map` of per-node results, then renders
status overlays on top of the frozen graph.

---

## What it looks like

```
┌─ Trace: abc-123 · ✓ ok · 312ms ──────────────── [×] ─┐
│  fec3e00728c3bcd1...                                   │
│                                                        │
│  [Timeline ▼]  [Canvas]                                │  ← tab switcher
│                                                        │
│  ┌────────────────────── canvas ───────────────────┐   │
│  │                                                 │   │
│  │          [/api/v1/messages]                     │   │
│  │          ● request  2ms                         │   │
│  │              │                                  │   │
│  │     ┌────────┴────────┐                         │   │
│  │  [profile]        [balance]                     │   │
│  │  ✓ slot  0ms      ✓ slot  0ms                  │   │
│  │     │                 │                         │   │
│  │ [CrmAdapter]    [BankingAdapter]                │   │
│  │  ✓ 287ms          ✗ 60ms  ← red ring + tooltip │   │
│  │     └────────┬────────┘                         │   │
│  │          [Merge]                                │   │
│  │          ✓ 1ms                                  │   │
│  │              │                                  │   │
│  │          [Response]                             │   │
│  │          ✓ 2ms                                  │   │
│  └─────────────────────────────────────────────────┘   │
└────────────────────────────────────────────────────────┘
```

---

## Phases

### Phase 14b-0 — LiveView changes

| # | What | File |
|---|------|------|
| 0a | Add `canvas_tab` assign: `"timeline" \| "canvas"` | `flow_audit_live.ex` |
| 0b | Add `handle_event("switch_tab", ...)` | `flow_audit_live.ex` |
| 0c | Add `FlowAudit.node_stats/1` — build `%{"n1" => %{status, duration_ms, reason}}` from trace | `infra_repo/flow_audit.ex` |
| 0d | On `select_event`, compute `node_stats` and push canvas init event | `flow_audit_live.ex` |

### Phase 14b-1 — Canvas hook (JS)

**New hook:** `FlowAuditCanvas` in `assets/js/hooks.js`

Receives two push_events:
- `"audit_canvas_init"` — `{nodes, connections, trace_map}` where `trace_map` is `%{"n1" => {status, duration_ms, reason}}`
- `"audit_canvas_clear"` — reset canvas

Rendering:
1. Initialize jsPlumb in read-only mode (no drag, no connector creation)
2. Lay out nodes at their saved `x`/`y` positions from `canvas_json`
3. Draw connections as static SVG paths (no hover handles)
4. For each node overlay a status badge:
   - ✓ green ring + "Xms" label → `status == "ok"`
   - ✗ red ring + "Xms" label → `status == "error"`
   - — grey ring + "—" label → `status == "skipped"` or not in trace
5. Error tooltip on hover → show `reason` text

### Phase 14b-2 — HEEx template changes

**File:** `flow_audit_live.html.heex` drawer body

Replace the flat node timeline section with a tabbed view:

```heex
<%!-- Tab switcher --%>
<div class="flex gap-1 border-b border-slate-100 px-5 pt-3">
  <button class={tab_class(@canvas_tab == "timeline")} phx-click="switch_tab" phx-value-tab="timeline">Timeline</button>
  <button class={tab_class(@canvas_tab == "canvas")}   phx-click="switch_tab" phx-value-tab="canvas">Canvas</button>
</div>

<%!-- Timeline tab (existing content) --%>
<%= if @canvas_tab == "timeline" do %>
  ... existing node timeline markup ...
<% end %>

<%!-- Canvas tab --%>
<%= if @canvas_tab == "canvas" do %>
  <div id="audit-canvas-container" class="relative h-96 overflow-hidden bg-slate-50 border-b border-slate-100">
    <div id="audit-flow-canvas" class="w-full h-full" phx-hook="FlowAuditCanvas" phx-update="ignore"></div>
  </div>
<% end %>
```

### Phase 14b-3 — CSS

Add minimal node overlay styles to `assets/css/app.css`:
- `.audit-node` — absolute positioned node card (read-only, pointer-events: none for drag)
- `.audit-node--ok` — green left border
- `.audit-node--error` — red left border + shake animation
- `.audit-node--skipped` — grey, opacity 50%
- `.audit-node__badge` — small pill top-right of node card

---

## File change summary

| Action | File |
|--------|------|
| MOD | `apps/gateway_web/lib/gateway_web_web/live/flow_audit_live.ex` |
| MOD | `apps/gateway_web/lib/gateway_web_web/live/flow_audit_live.html.heex` |
| MOD | `apps/infra_repo/lib/infra_repo/flow_audit.ex` — add `node_stats/1` |
| MOD | `apps/gateway_web/assets/js/hooks.js` — add `FlowAuditCanvas` hook |
| MOD | `apps/gateway_web/assets/css/app.css` — add overlay styles |

---

## Out of scope

- Edge highlighting (executed vs skipped paths) — requires tracking which connections
  were traversed; DAG executor does not currently record this. Future phase.
- Side-by-side diff of two executions — future phase.
