feat(knowledge-flywheel): add Phase 3 Knowledge Flywheel — AI analysis, review queue, analytics

Phase 3 implementation:
- AI session analysis service that generates flow proposals from resolved sessions
- APScheduler job for batch processing pending analyses (max_instances=1)
- Knowledge gap detection (weak options, high escalation signals)
- Flow proposals CRUD with team admin review workflow (approve/edit/dismiss/reject)
- FlowPilot analytics dashboard with confidence tiers, PSA metrics, knowledge gaps
- In-session script generator component
- Review queue page with filtering and proposal detail panel

Bug fixes from review (12 total):
- Fix "Edit & Publish" navigating to non-existent /editor/new route
- Hide Approve button for enhancement proposals (require Edit & Publish)
- Add max_instances=1 to scheduler to prevent TOCTOU race
- Fix eventual_success case() double-counting failed retries
- Add tree_structure validation before creating tree from proposal
- Simplify script generator rendering condition
- Add severity style fallback, toFixed on rates, Link instead of <a href>
- Add toast.warning on dismiss failure, fix dedup for domain-less sessions
- Cast Decimal to int in knowledge gap evidence dicts

Also updates CLAUDE.md with lessons 67-71 and Phase 3 project structure.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-19 05:12:10 +00:00
parent ce118b51d8
commit 9bad49d568
42 changed files with 5427 additions and 13 deletions

View File

@@ -0,0 +1,41 @@
import apiClient from './client'
import type {
FlowProposalSummary,
FlowProposalDetail,
FlowProposalStats,
ReviewProposalRequest,
} from '@/types/flow-proposal'
export const flowProposalsApi = {
async list(params?: {
status?: string
type?: string
domain?: string
sort_by?: string
skip?: number
limit?: number
}): Promise<FlowProposalSummary[]> {
const response = await apiClient.get<FlowProposalSummary[]>('/flow-proposals', { params })
return response.data
},
async getStats(): Promise<FlowProposalStats> {
const response = await apiClient.get<FlowProposalStats>('/flow-proposals/stats')
return response.data
},
async get(id: string): Promise<FlowProposalDetail> {
const response = await apiClient.get<FlowProposalDetail>(`/flow-proposals/${id}`)
return response.data
},
async review(id: string, data: ReviewProposalRequest): Promise<FlowProposalDetail> {
const response = await apiClient.post<FlowProposalDetail>(
`/flow-proposals/${id}/review`,
data
)
return response.data
},
}
export default flowProposalsApi

View File

@@ -0,0 +1,20 @@
import apiClient from './client'
import type { FlowPilotDashboard, KnowledgeGapReport } from '@/types/flowpilot-analytics'
export const flowpilotAnalyticsApi = {
async getDashboard(period: string = '30d'): Promise<FlowPilotDashboard> {
const response = await apiClient.get<FlowPilotDashboard>('/analytics/flowpilot', {
params: { period },
})
return response.data
},
async getKnowledgeGaps(period: string = '30d'): Promise<KnowledgeGapReport> {
const response = await apiClient.get<KnowledgeGapReport>('/analytics/flowpilot/knowledge-gaps', {
params: { period },
})
return response.data
},
}
export default flowpilotAnalyticsApi

View File

@@ -25,3 +25,5 @@ export { integrationsApi, sessionPsaApi } from './integrations'
export { sidebarApi } from './sidebar'
export { sessionToFlowApi } from './sessionToFlow'
export { aiSessionsApi } from './aiSessions'
export { flowProposalsApi } from './flowProposals'
export { flowpilotAnalyticsApi } from './flowpilotAnalytics'