# Execution-Tree Node Detail Modal (Design)

**Date:** 2026-05-22
**Status:** Validated — ready for implementation planning.

## Problem

On the Central UI execution-chain tree page (`/audit/execution-tree`, the
`ParentExecutionId` feature's Task 10), each node represents one execution and
shows a small inline summary. The only interaction is the short-id link, which
navigates away to `/audit/log?executionId=…`. There is no way to inspect an
execution's actual audit rows without leaving the tree.

## Decision

Double-clicking a tree node opens a **modal** showing that execution's audit
rows. The modal mirrors the `/audit/log` detail experience: a list of the
execution's rows, and clicking a row reveals that row's full field/payload
detail — the exact content the Audit Log drilldown drawer shows.

Resolved during brainstorming:
- **Modal content** — the execution's audit rows, with per-row full detail.
- **Multi-row executions** — list the rows; clicking one shows its detail. A
  single-row execution opens straight to the detail view.
- **Trigger** — double-click anywhere on the node. The short-id link keeps its
  single-click navigation to the Audit Log grid (unchanged).

### Considered and rejected

- **Reuse `AuditDrilldownDrawer` directly.** The drawer renders one
  `AuditEvent` by design; bending it into a list-or-detail hybrid is more
  invasive to a well-tested component than a purpose-built modal.
- **Inline expansion under the node.** The user asked for a modal, and an
  inline panel inside the recursive tree fights the existing expand/collapse
  toggle and is visually messy.

## Components

| Component | Change |
|---|---|
| `AuditEventDetail.razor` | **New.** The single-`AuditEvent` field/payload/drill-in-button block, extracted verbatim from `AuditDrilldownDrawer`'s body. |
| `AuditDrilldownDrawer.razor` | **Modified.** Keeps its offcanvas chrome + close button; its body becomes `<AuditEventDetail Event="Event" />`. The one refactor with regression risk — existing drawer bUnit + Playwright tests guard it. |
| `ExecutionDetailModal.razor` (+ `.razor.cs` + `.razor.css`) | **New.** A custom Bootstrap modal — hand-rolled `modal` / `modal-backdrop` markup, Blazor-toggled, no component framework (the same way `AuditDrilldownDrawer` hand-rolls `offcanvas`). |
| `ExecutionTree.razor` / `.razor.cs` | **Modified.** `@ondblclick` on the node body invokes a new `OnNodeActivated` `EventCallback<Guid>`; recursive child instances re-raise it upward so the event bubbles to the root. |
| `ExecutionTreePage.razor` / `.razor.cs` | **Modified.** Hosts one `ExecutionDetailModal`; wires the tree's `OnNodeActivated` to open it. |

No database, repository, or service changes — purely Central UI. The
`IAuditLogQueryService.QueryAsync` method already filters by `ExecutionId`; the
modal reuses it (no new service method).

## Data flow

1. Double-click a node → `ExecutionTree` invokes `OnNodeActivated(node.ExecutionId)`.
2. The event bubbles up the recursive `ExecutionTree` instances to
   `ExecutionTreePage`.
3. The page opens `ExecutionDetailModal` with the `ExecutionId`.
4. The modal calls `IAuditLogQueryService.QueryAsync(new AuditLogQueryFilter(ExecutionId: id), new AuditLogPaging(PageSize: 100))` → `IReadOnlyList<AuditEvent>`.
5. Render by row count:
   - **≥ 2 rows** — a compact row list (kind / status / target / time, each row a button); clicking a row swaps to its `<AuditEventDetail>` with a "← Back to rows" control.
   - **1 row** — opens straight to the detail view.
   - **0 rows** — a stub execution; a friendly empty state.
6. Close via the X button, the backdrop, or Esc.

The list rows are full `AuditEvent` objects (that is what `QueryAsync` returns),
so the list→detail transition needs no second fetch.

## Error handling

- A `QueryAsync` failure surfaces an inline error inside the modal ("Couldn't
  load this execution's rows") and never tears down the SignalR circuit —
  mirroring the tree page's existing `try/catch` degrade-gracefully pattern.
- An empty result renders the friendly empty state, not an error.

## Testing

- **bUnit** — `ExecutionTree` raises `OnNodeActivated` on `@ondblclick` and
  bubbles it through a nested instance; `ExecutionDetailModal` list renders from
  a fake query service, row click → detail, 1-row jump-straight, 0-row empty
  state, close; `AuditEventDetail` renders the field block; the existing
  `AuditDrilldownDrawer` tests stay green after the body extraction.
- **Playwright** — on `/audit/execution-tree`, double-click a node → modal opens
  → (multi-row) row list → click a row → detail → close. Uses a seeded chain.
- `frontend-design` skill for the modal markup/CSS — clean corporate aesthetic,
  custom Blazor + Bootstrap, no component frameworks.

## Constraints

- Central UI only — no DB / repository / service-contract changes.
- Custom Blazor + Bootstrap; no component frameworks.
- The short-id link's single-click navigation to `/audit/log?executionId=…` is
  unchanged.
