# Learning UI Redesign Spec

**Date**: 2026-02-22
**Source**: Multi-model synthesis (Opus, Grok, Gemini via Vario)
**Target**: `learning/schema/app.py`

## Problem

Current UI is organized around DB tables, not user tasks. Redundant columns (ID, Conf, Idx), detail panel below table forces scrolling, no search despite FTS5 availability, no cross-tab navigation.

## Tab Structure (4 tabs, down from 5)

| # | Tab | Purpose | Change |
|---|-----|---------|--------|
| 1 | **Principles** | Browse + effectiveness inline | Merged old Effectiveness tab in |
| 2 | **Triage** | Review raw/unlinked instances | Reframed old Instances as inbox |
| 3 | **Failures** | Error patterns, repairs | Same scope, cleaned columns |
| 4 | **Activity** | Recent applications, creations | Added drill-down navigation |

**Eliminated**: Standalone Effectiveness tab (effectiveness is a property of a principle, not a separate concept).

## Global Elements

### Search Bar (above tabs, always visible)

```
gr.Row():
  gr.Textbox(placeholder="Search principles, instances, failures…", scale=6)
  gr.Dropdown(choices=["All", "Principles", "Instances"], value="All", scale=1)
  gr.Button("🔍", scale=0)
```

Uses FTS5 tables. Results replace active tab's table. "✕ Clear" button restores default.

### Stats Strip (replaces big HTML cards)

Compact inline chips, each clickable to navigate:

```
📖 247 raw → Triage tab, queue=Raw
📐 14 orphan → Principles tab, filter instances=0
📊 72% rate → Principles tab, sort by rate
📡 3 today → Activity tab, days=1
```

Implementation: Row of `gr.Button(size="sm", variant="secondary")` styled as pills.

## Layout: Side-by-Side Master/Detail (all tabs)

```
┌──────────────────────────────────────────────────────┐
│ [Filters row]                                        │
├──────────────────────────┬───────────────────────────┤
│                          │                           │
│   Table (scale=3)        │   Detail Panel (scale=2)  │
│                          │                           │
│                          │                           │
└──────────────────────────┴───────────────────────────┘
```

CSS: Detail panel gets `min-height: 400px; max-height: 80vh; overflow-y: auto; border-left: 1px solid #e0e0e0`.

## Tab 1: Principles

### Filters
Type dropdown, Status dropdown, Min Apps dropdown ("any", "1+", "5+", "10+"), Sort dropdown ("Most applied", "Highest rate", "Most instances", "Name A-Z").

### Table Columns

| Column | Width | Content |
|--------|-------|---------|
| **Name** | 40% | Principle name, truncated 50ch |
| **Type** | 8% | Colored emoji chip |
| **Rate** | 14% | `18/23 (78%)` — color-coded green/orange/red |
| **Instances** | 8% | Count of linked instances |
| **Level** | 8% | Abstraction 1-5 |
| **Status** | 10% | Chip: active/proposed/deprecated |

**Dropped**: ID (hidden state), File (detail panel), separate Applied column (merged into Rate).

### Detail Panel

1. **Header**: Name as `<h3>`, type chip, level, status
2. **Effectiveness bar**: Green/orange/red segments with numbers (only if apps > 0)
3. **Text**: Full principle text
4. **Rationale** (if present)
5. **Anti-pattern** (if present): Red-bordered box
6. **Source file**: `<code>` path — small, gray
7. **Linked Instances** (`<details open>`): Up to 10, sorted by strength — content preview, project, link type, strength bar
8. **Recent Applications** (`<details>`): Last 5 with outcome icon, date, project, notes

## Tab 2: Triage

### Queue Dropdown (replaces generic status filter)
- **Raw** (default): `WHERE status = 'raw'`
- **Unlinked**: `WHERE id NOT IN (SELECT instance_id FROM instance_principle_links)`
- **Recent**: `WHERE created_at > datetime('now', '-3 days')`
- **All**: No filter

Plus: Project dropdown, Type dropdown.

### Table Columns

| Column | Width | Content |
|--------|-------|---------|
| **Content** | 50% | First 120 chars |
| **Type** | 10% | Emoji chip |
| **Source** | 10% | session/manual/mined/skill |
| **Project** | 15% | Project name |
| **Created** | 15% | Relative: "2h ago", "Yesterday", "Feb 12" |

**Dropped**: ID, Status (implied by queue), Conf, Links (detail panel).

### Detail Panel

1. Full content in styled box
2. Metadata line: type, source, project, confidence, date
3. Context snippet (collapsible)
4. Linked Principles (clickable → navigate to Principles tab)
5. Source Failure Pair (if exists, clickable → Failures tab)

## Tab 3: Failures

### Filters
Category, Verdict, Project dropdowns.

### Table Columns

| Column | Width | Content |
|--------|-------|---------|
| **Prompt** | 40% | context_prompt[:80] |
| **Category** | 15% | error_category |
| **Verdict** | 12% | Colored chip (repair/investigation/unrelated) |
| **Attempts** | 8% | Count |
| **Resolved** | 8% | ✅/❌ |
| **Linked** | 8% | 📖 if has instance |
| **Time** | 9% | Short date |

**Dropped**: ID, Conf (detail), Best Idx (detail), Project (filter).

### Detail Panel
Same structure as current but with verdict banner at top, navigation links to Principles/Triage.

## Tab 4: Activity

### Filters
Days dropdown (1/3/7/14/30), Event type dropdown (All/Applications/Instances/Principles).

### Table Columns

| Column | Width | Content |
|--------|-------|---------|
| **Time** | 12% | Relative time |
| **Event** | 8% | Icon only |
| **Subject** | 45% | Name/content preview |
| **Outcome** | 10% | Colored chip |
| **Project** | 12% | Name |
| **Source** | 13% | Short label |

### Detail Panel
Item summary + "→ View in [Tab]" navigation button.

**Key fix**: Store `ref_id` and `event_type` in hidden DataFrame columns for drill-down.

## Cross-Cutting UX

1. **Hidden ID columns**: IDs in DataFrame but CSS `display:none` on first col. Click handlers read by row index.
2. **Relative timestamps**: Helper `_relative_time()` returns "2h ago", "Yesterday", "Feb 12".
3. **Cross-tab navigation**: Detail panels include buttons that switch tabs + filter. Use `gr.State` for passing IDs.
4. **Empty states**: Friendly messages when tables have no rows.
5. **Pre-populated filters**: Synchronous DB queries at app definition time for `value=` params.
6. **URL deep linking**: `?tab=` param preserved (extend to `?tab=principles&filter=...`).
