# Dashboard Log Polling and Rendering Documentation

## Overview

This documentation explains the complete flow of how dashboard messages (logs with `operationId`) are polled, processed, sorted, and rendered in the workflow dashboard. The system uses a hierarchical tree structure to display operations and their progress, with real-time updates through polling.

## Architecture Flow

The system follows this flow:

1. **Polling Controller** (`workflowPollingController.js`) - Manages polling intervals and scheduling
2. **Data Layer** (`workflowData.js`) - Fetches data from API and routes logs to appropriate handlers
3. **Dashboard Processor** (`workflowUiRendererDashboard.js`) - Processes logs with `operationId` and builds hierarchical tree
4. **Dashboard Renderer** (`workflowUiRendererDashboard.js`) - Renders the hierarchical tree structure

## Key Files

- `workflowPollingController.js` - Centralized polling controller
- `workflowData.js` - API communication and data routing
- `workflowUiRendererDashboard.js` - Dashboard log processing and rendering
- `workflowCoordination.js` - State management coordination

## Implementation Details

### 1. Polling Mechanism

**File**: `frontend_agents/public/js/modules/workflowPollingController.js`

The polling controller uses a recursive `setTimeout` approach to create an infinite polling chain. This ensures continuous updates while preventing race conditions and rate limiting issues.

#### Configuration

- **Base interval**: 5 seconds (`baseInterval = 5000`)
- **Maximum interval**: 10 seconds (`maxInterval = 10000`)
- **Exponential backoff multiplier**: 1.5
- **Concurrency prevention**: Uses `isPollInProgress` flag to prevent multiple simultaneous polls

#### Key Methods

**`startPolling(workflowId)`**
- Starts polling for a specific workflow
- Stops any existing polling before starting new one
- Sets `activeWorkflowId` and `isPolling` flag
- Executes immediate first poll (no delay)
- Validates workflow ID before starting

**`doPolling()`**
- Executes one poll cycle asynchronously
- Prevents concurrent execution using `isPollInProgress` flag
- Calls `pollWorkflowData()` from `workflowData.js`
- Handles errors and implements exponential backoff on failures
- Self-schedules next poll using recursive `setTimeout`
- Validates workflow is still valid before scheduling next poll

**`stopPolling()`**
- Stops all polling operations immediately
- Clears all scheduled timeouts
- Resets all state flags (`isPolling`, `isPollInProgress`, `activeWorkflowId`)
- Resets failure count

**`pausePolling()` / `resumePolling()`**
- Temporarily pauses polling (e.g., during user interactions)
- Resumes polling after pause

#### Polling Flow

```javascript
startPolling(workflowId)
  ↓
doPolling() [immediate first poll]
  ↓
pollWorkflowData(workflowId) [async API call]
  ↓
setTimeout(() => doPolling(), interval) [schedule next poll]
  ↓
[recursive loop continues until stopped]
```

#### Error Handling

- **Rate limiting (429 errors)**: Increases backoff more aggressively, stops polling after 5 consecutive rate limit errors
- **Network errors**: Logged but don't immediately stop polling (allows retry)
- **Workflow validation**: Checks if workflow is still valid before each poll cycle
- **Poll failures**: Exponential backoff increases interval up to `maxInterval`

### 2. Data Fetching

**File**: `frontend_agents/public/js/modules/workflowData.js`

The `pollWorkflowData()` function orchestrates the data fetching process.

#### API Calls

The function makes two parallel API calls:

1. **`api.getWorkflow(workflowId)`** - Fetches workflow status and metadata
2. **`api.getWorkflowChatData(workflowId, afterTimestamp)`** - Fetches unified chat data (messages, logs, stats)

#### Incremental Polling

- **First poll**: `afterTimestamp = null` → Fetches ALL historical data
- **Subsequent polls**: `afterTimestamp = workflowState.lastRenderedTimestamp` → Fetches only new items since last render
- **Timestamp tracking**: Uses `createdAt` timestamp from each item to track what's been rendered

#### Data Processing

The `processUnifiedChatData()` function processes items in chronological order:

1. Routes each item based on `type` field:
   - `'message'` → `processUnifiedMessage()`
   - `'log'` → `processUnifiedLog()`
   - `'stat'` → `processUnifiedStat()`

2. Updates `lastRenderedTimestamp` after processing each item (ensures accurate incremental polling)

3. Processes items sequentially to maintain chronological order

#### Workflow Status Updates

- Monitors workflow status changes
- Updates UI buttons and controls when status changes
- Handles special case: Ignores 'completed' status if workflow is in Round 2+ (prevents premature stopping)

#### Polling Continuation Logic

Polling continues based on workflow status:
- **'running'**: Continues polling
- **'completed'**: Continues polling temporarily to get final messages, then stops
- **'failed' / 'stopped'**: Stops polling immediately
- **Other statuses**: Stops polling

### 3. Log Routing

**File**: `frontend_agents/public/js/modules/workflowData.js` - `processUnifiedLog()`

Logs are routed to different rendering areas based on the presence of `operationId`:

#### Routing Logic

```javascript
if (log.operationId) {
    // Logs WITH operationId → Dashboard
    processDashboardLogs([frontendLog]);
} else {
    // Logs WITHOUT operationId → Unified Content Area
    WorkflowCoordination.addLogEntry(frontendLog.message, frontendLog.type, frontendLog);
}
```

#### Log Format Conversion

Backend `ChatLog` format is converted to frontend format:

```javascript
{
    id: log.id,
    message: log.message,
    type: log.type || 'info',
    timestamp: log.timestamp,
    status: log.status || 'running',
    progress: log.progress !== undefined && log.progress !== null ? log.progress : undefined,
    performance: log.performance,
    operationId: log.operationId || null,
    parentId: log.parentId || null
}
```

#### Key Points

- **All logs are processed**: No duplicates are skipped (logs may contain progress updates)
- **Progress tracking**: Logs with `operationId` typically contain progress information
- **Hierarchical structure**: `parentId` field enables parent-child relationships between operations

### 4. Dashboard Log Processing

**File**: `frontend_agents/public/js/modules/workflowUiRendererDashboard.js` - `processDashboardLogs()`

This function processes logs with `operationId` and builds the hierarchical tree structure.

#### Processing Steps

1. **Group by operationId**
   - Creates or updates operation groups in `dashboardLogTree.operations` Map
   - Each operation stores logs in a Map keyed by `logId` (ensures uniqueness)

2. **Update operation metadata**
   - Updates `parentId` if not set yet (from first log entry)
   - Updates `latestProgress` when log contains progress value
   - Updates `latestStatus` when log contains status value

3. **Generate unique log IDs**
   - Uses provided `log.id` if available
   - Otherwise generates: `log_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`
   - Ensures all progress updates are stored, even with same progress value

4. **Build root operations list**
   - Filters operations without `parentId`
   - Stores in `dashboardLogTree.rootOperations` array

5. **Trigger rendering**
   - Calls `renderDashboard()` after processing all logs

#### Data Structure

```javascript
dashboardLogTree = {
    operations: Map<operationId, {
        logs: Map<logId, log>,           // All logs for this operation
        parentId: string | null,          // Parent operation ID (if nested)
        expanded: boolean,                 // UI expanded/collapsed state
        latestProgress: number | null,    // Most recent progress value
        latestStatus: string | null       // Most recent status value
    }>,
    rootOperations: string[],             // Operation IDs without parent
    logExpandedStates: Map<logId, boolean>, // Individual log expanded states
    currentRound: number | null           // Current workflow round
}
```

#### Important Behaviors

- **All logs stored**: Every log with same `operationId` is stored (represents progress updates)
- **Latest values tracked**: `latestProgress` and `latestStatus` always reflect most recent state
- **Parent-child relationships**: Operations can nest via `parentId` field

### 5. Sorting

**File**: `frontend_agents/public/js/modules/workflowUiRendererDashboard.js`

Multiple sorting mechanisms ensure consistent display order:

#### Operation-Level Log Sorting

**Location**: `renderOperationNode()` function, lines 169-173

Logs within an operation are sorted by timestamp in ascending order:

```javascript
const logsArray = Array.from(operation.logs.values()).sort((a, b) => {
    const tsA = a.timestamp || 0;
    const tsB = b.timestamp || 0;
    return tsA - tsB;  // Ascending order (oldest first)
});
```

**Purpose**: Ensures logs are displayed in chronological order within each operation.

#### Child Operations Sorting

**Location**: `getChildOperations()` function, line 453

Child operations are sorted alphabetically by `operationId`:

```javascript
return Array.from(dashboardLogTree.operations.entries())
    .filter(([opId, op]) => op.parentId === parentId)
    .map(([opId]) => opId)
    .sort(); // Alphabetical sort for consistent ordering
```

**Purpose**: Provides consistent, predictable ordering of sibling operations.

#### Timeline Sorting (Unified Content)

**Location**: `workflowUiRenderer.js` - `renderUnifiedContent()` function

Logs without `operationId` are combined with messages and sorted by timestamp:

```javascript
timeline.sort((a, b) => a.timestamp - b.timestamp);
```

**Purpose**: Creates a unified chronological timeline of all non-dashboard content.

#### Sorting Summary

| Context | Sort Key | Order | Purpose |
|---------|----------|-------|---------|
| Logs within operation | `timestamp` | Ascending | Chronological display |
| Child operations | `operationId` | Alphabetical | Consistent ordering |
| Unified timeline | `timestamp` | Ascending | Chronological timeline |

### 6. Rendering

**File**: `frontend_agents/public/js/modules/workflowUiRendererDashboard.js` - `renderDashboard()`

The rendering system creates a hierarchical tree structure with collapsible nodes and progress indicators.

#### Hierarchical Structure

- **Root operations**: Operations without `parentId` are rendered first
- **Child operations**: Operations with `parentId` matching a parent's `operationId` are nested
- **Single line per operation**: Each operation shows ONE line that updates with latest status/progress
- **All logs represented**: All logs with same `operationId` are represented by this single updating line

#### Rendering Process

**Step 1: `renderDashboard()`**
- Builds HTML from `dashboardLogTree` structure
- Handles empty state (no operations)
- Sets up event handlers for collapse/expand functionality

**Step 2: `renderOperationNode(operationId, depth)`** (Recursive)
- Renders a single operation node
- Calculates indentation based on depth (8px per level)
- Determines if operation has child operations
- Gets latest log entry for operation name and type
- Calculates progress percentage (forces 100% when status is 'completed')
- Builds HTML for:
  - Expand/collapse button (if has children)
  - Operation icon (based on log type)
  - Operation name (from latest log message)
  - Status and progress percentage
  - Progress bar (if progress available)
- Recursively renders child operations if expanded

#### Visual Elements

**Operation Header**
- Expand/collapse button (chevron icon)
- Operation icon (info/success/error/warning)
- Operation name (from latest log message)
- Status badge (running/completed/failed/etc.)
- Progress percentage (if available)

**Progress Bar**
- Visual progress indicator
- Width based on progress percentage (0-100%)
- "completed" class when progress >= 100%
- Hidden if no progress value

**Indentation**
- Root level (depth 0): No indentation
- Child levels: Indented via parent container padding (8px per level)
- Creates visual hierarchy

#### State Management

**Expanded/Collapsed State**
- Stored in `operation.expanded` boolean
- Toggled via `toggleOperationExpanded(operationId)`
- Persists during re-renders
- Controls visibility of child operations container

**Event Handlers**
- `setupCollapseExpandHandlers()`: Sets up click handlers for expand buttons
- `setupLogCollapseExpandHandlers()`: Sets up handlers for log entry expansion
- Click handlers toggle expanded state and re-render dashboard

#### Rendering Flow

```
renderDashboard()
  ↓
[For each root operation]
  renderOperationNode(operationId, 0)
    ↓
    [Build operation header HTML]
    ↓
    [If has children and expanded]
      [For each child operation]
        renderOperationNode(childOperationId, depth)
          ↓
          [Recursive rendering continues...]
    ↓
[Set innerHTML of dashboard container]
  ↓
[Setup event handlers]
```

#### Key Rendering Features

1. **Progress Updates**: Operation line updates in-place as new logs arrive
2. **Status Changes**: Status badge updates when operation status changes
3. **Collapsible Tree**: Users can expand/collapse operation groups
4. **Visual Hierarchy**: Indentation shows parent-child relationships
5. **Latest State**: Always shows most recent log message, progress, and status

## Data Structures

### Dashboard Log Tree

```javascript
{
    operations: Map<operationId, {
        logs: Map<logId, log>,           // All logs for this operation
        parentId: string | null,          // Parent operation ID
        expanded: boolean,                 // UI expanded state
        latestProgress: number | null,    // Most recent progress (0-1)
        latestStatus: string | null       // Most recent status
    }>,
    rootOperations: string[],             // Operation IDs without parent
    logExpandedStates: Map<logId, boolean>, // Individual log expanded states
    currentRound: number | null           // Current workflow round
}
```

### Log Entry Format

```javascript
{
    id: string,                           // Unique log ID
    message: string,                      // Log message text
    type: 'info' | 'success' | 'error' | 'warning',
    timestamp: number,                    // Unix timestamp (seconds)
    status: string,                       // Operation status
    progress: number | null,              // Progress value (0-1) or null
    operationId: string | null,          // Operation ID (null = unified content)
    parentId: string | null              // Parent operation ID (for nesting)
}
```

### Unified Chat Data Item

```javascript
{
    type: 'message' | 'log' | 'stat',    // Item type
    item: { /* message/log/stat data */ },
    createdAt: number                     // Timestamp for sorting
}
```

## Key Features

### 1. Incremental Polling

- Uses `lastRenderedTimestamp` to fetch only new items
- First poll loads all historical data (`afterTimestamp = null`)
- Subsequent polls fetch incrementally (`afterTimestamp = lastRenderedTimestamp`)
- Reduces API load and improves performance

### 2. Hierarchical Display

- Operations can have parent-child relationships via `parentId`
- Visual indentation shows hierarchy
- Collapsible tree structure for better UX
- Supports unlimited nesting depth

### 3. Progress Tracking

- Shows progress bars for operations with progress values
- Updates in real-time as new logs arrive
- Forces 100% progress when status is 'completed'
- Displays status badges (running/completed/failed/etc.)

### 4. Collapsible Tree

- Users can expand/collapse operation groups
- Expand/collapse state persists during re-renders
- Click handlers on operation headers and expand buttons
- Smooth visual transitions

### 5. Round Detection

- Tracks current workflow round in `dashboardLogTree.currentRound`
- Clears dashboard when round changes (via `updateProgressFromMessage()`)
- Prevents mixing data from different workflow rounds

### 6. Duplicate Prevention

- Uses Map with `logId` keys to prevent duplicate entries
- Same log ID updates in place rather than creating duplicates
- Ensures unique log entries even with same progress value

## Error Handling

### Rate Limiting (429 Errors)

- Detected in `pollWorkflowData()` and `doPolling()`
- Triggers exponential backoff with increased multiplier
- Stops polling after 5 consecutive rate limit errors
- Prevents API abuse

### Network Errors

- Logged but don't immediately stop polling
- Allows retry on transient network issues
- Controller handles backoff automatically
- Polling continues for recoverable errors

### Rendering Errors

- Don't stop polling (UI issue, not data issue)
- Logged for debugging
- Polling continues to get workflow status updates
- UI can recover on next successful render

### Workflow Validation

- `isWorkflowValid()` checks before each poll cycle
- Validates workflow state exists and matches active workflow
- Checks if polling is still enabled (`pollActive` flag)
- Stops polling if workflow is invalid

## Performance Considerations

### Polling Intervals

- Base interval: 5 seconds (balanced between responsiveness and server load)
- Maximum interval: 10 seconds (prevents excessive backoff)
- Exponential backoff: Prevents overwhelming server during errors

### Data Processing

- Processes items sequentially to maintain chronological order
- Uses Maps for O(1) lookups when grouping operations
- Incremental polling reduces data transfer
- Timestamp-based filtering at API level

### Rendering Optimization

- Full re-render on each update (simplifies state management)
- Event handlers re-attached after each render
- HTML generation is efficient (string concatenation)
- Minimal DOM manipulation (innerHTML replacement)

## Usage Examples

### Starting Polling

```javascript
import pollingController from './workflowPollingController.js';

// Start polling for a workflow
pollingController.startPolling('workflow-123');
```

### Stopping Polling

```javascript
// Stop polling
pollingController.stopPolling();
```

### Processing Dashboard Logs

```javascript
import { processDashboardLogs } from './workflowUiRendererDashboard.js';

// Process logs with operationId
const logs = [
    {
        id: 'log-1',
        message: 'Processing file...',
        type: 'info',
        timestamp: 1234567890,
        status: 'running',
        progress: 0.5,
        operationId: 'op-123',
        parentId: null
    }
];

processDashboardLogs(logs);
```

### Clearing Dashboard

```javascript
import { clearDashboard } from './workflowUiRendererDashboard.js';

// Clear dashboard (e.g., on workflow reset)
clearDashboard(true); // true = reset round tracking
```

## Related Documentation

- `FRONTEND_ARCHITECTURE.md` - Overall frontend architecture
- `workflowCoordination.js` - State management coordination
- `workflowUiRenderer.js` - Unified content rendering

## Conclusion

The dashboard log polling and rendering system provides a robust, hierarchical display of workflow operations with real-time updates. The system efficiently handles incremental polling, sorts data chronologically, and renders a collapsible tree structure that scales to complex workflows with multiple nested operations.

