feat: Learning page — capability timeline + trend, built on real data
Some checks failed
ci / build-test (push) Has been cancelled
ci / docker-build (push) Has been cancelled

The plan's "learning view" (runbook success-rate trends, promoted
skills, capability timeline) assumes the patterns/skills/feedback
pipeline is populated. It isn't: all three tables are empty in
production and nothing in the codebase ever writes to feedback, so
building the UI against them today would ship a permanently-empty
page. Scoped instead around data that's real and growing —
executions — while still wiring up /patterns and /skills so the page
needs no rework once that pipeline exists.

New /api/v1/learning/timeline (per-verb first-success date + success
rate, parsed via the existing splitAction helper) and
/api/v1/learning/trend (30-day daily success/fail counts), both
read-only queries against executions. Patterns and skills sections
call the existing (untouched) ListPatterns/ListSkills endpoints and
render an explanatory empty state instead of nothing.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-10 21:28:19 +02:00
parent 6192c35c10
commit 52e16e04ca
5 changed files with 380 additions and 0 deletions

View File

@@ -277,6 +277,72 @@ export async function fetchSessionDigest(sessionId: string): Promise<SessionDige
return res.json()
}
export interface CapabilityTimelineItem {
verb: string
first_success: string | null
successes: number
total: number
}
export async function fetchLearningTimeline(): Promise<CapabilityTimelineItem[]> {
const res = await fetch(`${API}/learning/timeline`)
if (!res.ok) return []
const data = await res.json()
return data.items ?? []
}
export interface TrendBucket {
day: string
successes: number
failures: number
}
export async function fetchLearningTrend(): Promise<TrendBucket[]> {
const res = await fetch(`${API}/learning/trend`)
if (!res.ok) return []
const data = await res.json()
return data.items ?? []
}
export interface Pattern {
id: string
slug: string
applies_type: string
action: string
pattern: string
confidence: number
evidence_count: number
success_count?: number
failure_count?: number
status: string
quarantined?: boolean
}
export async function fetchPatterns(): Promise<Pattern[]> {
const res = await fetch(`${API}/patterns`)
if (!res.ok) return []
const data = await res.json()
return data.items ?? []
}
export interface Skill {
id: string
slug: string
name: string
applies_type?: string | null
action: string
status: string
success_rate?: number | null
last_used_at?: string | null
}
export async function fetchSkills(): Promise<Skill[]> {
const res = await fetch(`${API}/skills`)
if (!res.ok) return []
const data = await res.json()
return data.items ?? []
}
export interface Signal {
id: string
slug: string