Merge PR #388: Daily Workflow Integration & Enhanced Reliability
- Resolve merge conflicts in backend/services/today_workflow_service.py and frontend/src/stores/workflowStore.ts - Backend: Keep robust handling for both dict and object types in TaskProposal conversion - Backend: Combine dependencies coercion with task metadata normalization - Frontend: Implement graceful fallback pattern (try server first, then local generation on unavailability) - Add provenanceSummary integration from server responses - Ensure degraded mode handling with appropriate messaging
This commit is contained in:
@@ -82,6 +82,15 @@ const WorkflowProgressBar: React.FC<WorkflowProgressBarProps> = ({
|
||||
return 'Ready to Start';
|
||||
};
|
||||
|
||||
const getProvenanceLabel = () => {
|
||||
const summary = currentWorkflow?.provenanceSummary;
|
||||
if (!summary) return 'Daily Workflow';
|
||||
if (summary.generationMode === 'agent_committee') return 'Personalized by Agents';
|
||||
if (summary.generationMode === 'llm_generation' && !summary.fallbackUsed) return 'AI Personalized Guide';
|
||||
if (summary.fallbackUsed || summary.generationMode === 'controlled_fallback') return 'Baseline Daily Guide';
|
||||
return 'Daily Workflow';
|
||||
};
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: -20 }}
|
||||
@@ -128,6 +137,16 @@ const WorkflowProgressBar: React.FC<WorkflowProgressBarProps> = ({
|
||||
fontWeight: 600
|
||||
}}
|
||||
/>
|
||||
<Chip
|
||||
label={getProvenanceLabel()}
|
||||
size="small"
|
||||
sx={{
|
||||
background: 'rgba(255,255,255,0.08)',
|
||||
color: 'rgba(255,255,255,0.9)',
|
||||
border: '1px solid rgba(255,255,255,0.2)',
|
||||
fontWeight: 600
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* Controls */}
|
||||
|
||||
@@ -164,32 +164,36 @@ export const useWorkflowStore = create<WorkflowState>()(
|
||||
// Generate daily workflow
|
||||
generateDailyWorkflow: async (userId: string, date?: string) => {
|
||||
set({ isLoading: true, error: null });
|
||||
|
||||
|
||||
try {
|
||||
const resp = await apiClient.get('/api/today-workflow', { params: date ? { date } : {} });
|
||||
const serverWorkflow = resp?.data?.data?.workflow as DailyWorkflow | undefined;
|
||||
|
||||
if (!serverWorkflow || !Array.isArray(serverWorkflow.tasks)) {
|
||||
throw new WorkflowError({
|
||||
code: 'WORKFLOW_SCHEMA_INVALID',
|
||||
message: 'Server workflow response is missing a valid tasks array.',
|
||||
timestamp: new Date(),
|
||||
recoverable: false,
|
||||
suggestedAction: 'Refresh and try again. If this persists, contact support.'
|
||||
const planSummary = resp?.data?.data?.plan?.provenance_summary;
|
||||
|
||||
if (serverWorkflow && Array.isArray(serverWorkflow.tasks)) {
|
||||
if (planSummary && !serverWorkflow.provenanceSummary) {
|
||||
serverWorkflow.provenanceSummary = planSummary;
|
||||
}
|
||||
const normalizedWorkflow = normalizeServerWorkflow(serverWorkflow);
|
||||
const derived = computeProgressAndNavigation(normalizedWorkflow);
|
||||
set({
|
||||
currentWorkflow: normalizedWorkflow,
|
||||
workflowProgress: derived.progress,
|
||||
navigationState: derived.navigation,
|
||||
isLoading: false,
|
||||
isDegradedMode: false,
|
||||
degradedModeReason: null,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const normalizedWorkflow = normalizeServerWorkflow(serverWorkflow);
|
||||
const derived = computeProgressAndNavigation(normalizedWorkflow);
|
||||
set({
|
||||
currentWorkflow: normalizedWorkflow,
|
||||
workflowProgress: derived.progress,
|
||||
navigationState: derived.navigation,
|
||||
isLoading: false,
|
||||
isDegradedMode: false,
|
||||
degradedModeReason: null,
|
||||
throw new WorkflowError({
|
||||
code: 'WORKFLOW_SCHEMA_INVALID',
|
||||
message: 'Server workflow response is missing a valid tasks array.',
|
||||
timestamp: new Date(),
|
||||
recoverable: false,
|
||||
suggestedAction: 'Refresh and try again. If this persists, contact support.'
|
||||
});
|
||||
return;
|
||||
} catch (error) {
|
||||
if (!isServerUnavailableError(error)) {
|
||||
set({
|
||||
|
||||
@@ -5,6 +5,14 @@ export type TaskStatus = 'pending' | 'in_progress' | 'completed' | 'skipped';
|
||||
export type TaskPriority = 'high' | 'medium' | 'low';
|
||||
export type ActionType = 'navigate' | 'modal' | 'external';
|
||||
export type WorkflowStatus = 'not_started' | 'in_progress' | 'completed' | 'paused' | 'stopped';
|
||||
export type WorkflowGenerationMode = 'agent_committee' | 'llm_generation' | 'llm_pillar_backfill' | 'controlled_fallback';
|
||||
|
||||
export interface WorkflowProvenanceSummary {
|
||||
generationMode: WorkflowGenerationMode;
|
||||
committeeAgentCount: number;
|
||||
fallbackUsed: boolean;
|
||||
taskSourceBreakdown: Partial<Record<WorkflowGenerationMode, number>>;
|
||||
}
|
||||
|
||||
export interface TodayTask {
|
||||
id: string;
|
||||
@@ -44,6 +52,7 @@ export interface DailyWorkflow {
|
||||
completedAt?: Date;
|
||||
totalEstimatedTime: number; // in minutes
|
||||
actualTimeSpent: number; // in minutes
|
||||
provenanceSummary?: WorkflowProvenanceSummary;
|
||||
}
|
||||
|
||||
export interface WorkflowProgress {
|
||||
@@ -54,6 +63,7 @@ export interface WorkflowProgress {
|
||||
nextTask?: TodayTask;
|
||||
estimatedTimeRemaining: number; // in minutes
|
||||
actualTimeSpent: number; // in minutes
|
||||
provenanceSummary?: WorkflowProvenanceSummary;
|
||||
}
|
||||
|
||||
export interface TaskCompletionData {
|
||||
|
||||
Reference in New Issue
Block a user