# Error Dashboard Phase 2 — Design Spec

## Overview

Phase 2 adds regression-based trend sorting, version tracking for root cause discovery, and caller graph visibility to the Error Dashboard. Builds on the Phase 1 listing + detail pages.

**Four features:**
1. Regression coefficient + trend sort (backend field + frontend sort)
2. Version track timeline (new component, version API integration)
3. Edge diff link (open existing diff modal)
4. Callers panel (traverse workspace sink, new sidebar component)

---

## 1. Regression Coefficient + Trend Sort

### Backend (separate job — spec only)

**New fields on `error_signature` table:**

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `regression_coeff` | float | 0 | Linear regression slope `a` from `f(y) = ax + b` on hourly data |
| `regression_r2` | float | 0 | R² goodness-of-fit |
| `regression_n` | int | 0 | Number of data points used |

**Computation contract:** Only set `regression_coeff` to a non-zero slope when `regression_n >= 5` AND `regression_r2 >= 0.3`. Otherwise leave at 0. This means signatures without a statistically meaningful trend naturally sort to the bottom (`coeff=0`) when using `sort=trend`.

**Sort parameter:** `GET /workspace/:id/error_signatures` accepts `sort` enum with values `last_seen_at` (default) and `trend` (orders by `regression_coeff` DESC).

### Frontend Changes

**Listing page:**
- Default sort switches to `sort=trend` — pass `sort: 'trend'` in the `ErrorDashboardLoadSignatures` action and through to the service call
- Add `sort` parameter to `ErrorDashboardLoadSignatures` action props and `getSignaturesForDashboard` service method

**Trend classification:**
- `classifyTrend` utility updated: when `regression_coeff` is available on the signature (non-zero), use it directly instead of frontend computation
- Fallback to frontend-computed slope when `regression_coeff === 0` (no backend data yet)
- Thresholds unchanged: `new` (first_seen < 4h), `escalating` (coeff > 1.2), `shrinking` (coeff < 0.8), `ongoing` (else)

**Optional future:** Display trend confidence using `regression_r2` and `regression_n` (e.g., "escalating (high confidence)" vs "escalating (low confidence)"). Not in scope for P2.

---

## 2. Version Track Timeline

### Component

**Name:** `ErrorVersionTrackComponent` (standalone)
**Location:** `src/app/workspace/components/error-version-track/`
**Placement:** Detail page, left column, between the 24h histogram and the statement link

### Data Flow

1. Detail page provides the error signature (has `obj.id`, `type`, `first_seen_at`)
2. Component maps `type` to API prefix: `query`/`function`/`task`/`middleware`/`trigger`
3. Fetches versions: `GET /{type}/{obj.id}/version?page=1` via `UserService`
4. Response: `{ items: IVersion[], nextPage }` where each version has `source.index`, `created_at`, `data`

### Timeline Rendering

- Horizontal line with version markers (circles) positioned proportionally by `created_at`
- Each version labeled with `source.index` (version number) below the marker
- Error occurrences from `hourly_counts` overlaid as small red dots on the same time axis
- **Suspect version highlight:** The version whose `created_at` is just before `first_seen_at` gets a distinct visual treatment (filled red circle, or red border) — this is the version that likely introduced the error
- Dashed vertical line at `first_seen_at` marking where errors began

### Inputs

```typescript
signature: InputSignal<IErrorSignature>   // provides obj.id, type, first_seen_at, hourly_counts
workspace: InputSignal<Workspace>         // for UserService context
```

### Empty State

If the object has 0 or 1 versions: "No version history available"

---

## 3. Edge Diff Link

### Trigger

"View edge diff" link displayed next to the suspect version on the version track timeline.

### Behavior

Opens `WorkspaceDiffModalComponent` in `version-history` mode, pre-selecting the suspect version. The user sees the diff between version N-1 → N where errors first appeared.

### Required Data for Modal

```typescript
{
  srcWorkspace: workspace,
  dstWorkspace: workspace,
  srcBranch: workspace.getEditBranch(),
  dstBranch: workspace.getEditBranch(),
  activeObject: { id: signature.obj.id },    // the function/query/task object
  activeObjectType: mapTypeToEObjectType(signature.type),  // 'query' → EObjectType.Query, etc.
  title: 'Error Regression — Version Diff',
  isCompare: false,
  isHistory: true,
}
```

The modal will load version history internally and display the diff viewer. The suspect version can be pre-selected via `activeChangeItem` if the modal's API supports it, otherwise the user selects from the version list.

### Type Mapping

| Signature `type` | `EObjectType` |
|-------------------|---------------|
| `query` | `EObjectType.Query` |
| `function` | `EObjectType.Function` |
| `task` | `EObjectType.Task` |
| `middleware` | `EObjectType.Middleware` |
| `trigger` | `EObjectType.Trigger` |

---

## 4. Callers Panel

### Component

**Name:** `ErrorCallersPanelComponent` (standalone)
**Location:** `src/app/workspace/components/error-callers-panel/`
**Placement:** Detail page, right sidebar, below Stack Issues panel

### Data Flow

1. Read workspace sink from `GetWorkspaceSinkSelector` (already in store, no API call)
2. Error signature provides `obj.id` and `type`
3. **Only relevant for `function` and `middleware` types** — queries are top-level API endpoints
4. Traverse all API stacks (`query` type objects) in the sink, walk their statement trees looking for references to `obj.id`
5. Return matching APIs with their HTTP `verb` and `uri`

### Display

Each caller rendered as a row:
- HTTP verb badge (colored: GET=blue, POST=green, PUT=orange, DELETE=red, PATCH=purple)
- Endpoint path (e.g., `/auth/me`)
- Clickable — navigates to the API endpoint's editor

### Edge Cases

| Condition | Display |
|-----------|---------|
| Error is on a `query` type | "This is a top-level API endpoint" |
| No callers found for function/middleware | "No callers found" |
| Sink not loaded yet | Loading spinner |

### Sink Traversal Logic

```
for each query in sink:
  walk query.statements recursively:
    if statement references obj.id (as function_id, middleware_id, etc.):
      add { verb: query.verb, uri: query.uri, id: query.id } to callers
```

This runs once as a computed signal — no repeated traversal.

---

## New Files

| File | Purpose |
|------|---------|
| `src/app/workspace/components/error-version-track/error-version-track.component.ts` | Version timeline component |
| `src/app/workspace/components/error-version-track/error-version-track.component.html` | Timeline template |
| `src/app/workspace/components/error-version-track/error-version-track.component.scss` | Timeline styles |
| `src/app/workspace/components/error-callers-panel/error-callers-panel.component.ts` | Callers panel component |
| `src/app/workspace/components/error-callers-panel/error-callers-panel.component.html` | Callers template |
| `src/app/workspace/components/error-callers-panel/error-callers-panel.component.scss` | Callers styles |

## Modified Files

| File | Change |
|------|--------|
| `src/app/workspace/utils/trend.util.ts` | Use `regression_coeff` when available |
| `src/app/workspace/actions/error-dashboard.action.ts` | Add `sort` param to LoadSignatures |
| `src/app/workspace/services/error-log-data.service.ts` | Pass `sort` param to API |
| `src/app/workspace/effects/error-dashboard.effect.ts` | Pass `sort` through to service |
| `src/app/workspace/pages/workspace-error-dashboard-page/workspace-error-dashboard-page.component.ts` | Default sort to 'trend' |
| `src/app/workspace/pages/workspace-error-detail-page/workspace-error-detail-page.component.ts` | Add version track + callers panel |
| `src/app/workspace/pages/workspace-error-detail-page/workspace-error-detail-page.component.html` | Layout new components |
| `src/app/workspace/interfaces/error-signature.interface.ts` | Add `regression_coeff`, `regression_r2`, `regression_n` fields |

---

## Backend Spec (for separate implementation)

### 1. Regression Computation

Add a periodic job or on-ingestion hook that for each active error signature:
1. Reads the `hourly_counts` (last 24h of hourly error counts)
2. Computes simple linear regression `f(y) = ax + b` where x = hour index, y = count
3. Stores `regression_coeff = a`, `regression_r2 = R²`, `regression_n = number of data points`
4. Only sets non-zero `regression_coeff` when `regression_n >= 5` AND `regression_r2 >= 0.3`

### 2. Sort Parameter

`GET /workspace/:id/error_signatures` — `sort` enum param:
- `last_seen_at` (default) — current behavior
- `trend` — ORDER BY `regression_coeff` DESC

Both already implemented.
