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

@@ -4,11 +4,13 @@ import { cn } from '@/lib/utils'
import type { AISessionStepResponse, StepResponseRequest } from '@/types/ai-session'
import { MarkdownContent } from '@/components/ui/MarkdownContent'
import { FlowPilotOptions } from './FlowPilotOptions'
import { InSessionScriptGenerator } from './InSessionScriptGenerator'
interface FlowPilotStepCardProps {
step: AISessionStepResponse
isCurrentStep: boolean
isProcessing: boolean
sessionId?: string
onRespond: (response: StepResponseRequest) => void
}
@@ -22,7 +24,7 @@ const STEP_TYPE_ICONS = {
note: MessageSquare,
} as const
export function FlowPilotStepCard({ step, isCurrentStep, isProcessing, onRespond }: FlowPilotStepCardProps) {
export function FlowPilotStepCard({ step, isCurrentStep, isProcessing, sessionId, onRespond }: FlowPilotStepCardProps) {
const [freeText, setFreeText] = useState('')
const [showFreeText, setShowFreeText] = useState(false)
const [isCollapsed, setIsCollapsed] = useState(!isCurrentStep)
@@ -163,8 +165,19 @@ export function FlowPilotStepCard({ step, isCurrentStep, isProcessing, onRespond
/>
)}
{/* In-session script generator */}
{!isResolutionSuggestion && (content.action_type as string) === 'script_generation' && sessionId && (
<InSessionScriptGenerator
templateId={(content.template_id as string) || ''}
preFilledParams={(content.pre_filled_params as Record<string, string>) || {}}
instructions={(content.instructions as string) || stepText}
sessionId={sessionId}
onRespond={onRespond}
/>
)}
{/* Action step buttons */}
{!isResolutionSuggestion && step.step_type === 'action' && (
{!isResolutionSuggestion && step.step_type === 'action' && (content.action_type as string) !== 'script_generation' && (
<div className="flex gap-2">
<button
onClick={() => handleActionComplete(true)}