feat: Learning page — capability timeline + trend, built on real data
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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user