# Chunk 5: Build Status **Parent:** [Build Graph Dashboard](../build-graph-dashboard.md) **Previous:** [Chunk 4: Recent Activity](./chunk-4-recent-activity.md) **Next:** [Chunk 6: Partition Pages](./chunk-6-partition-pages.md) ## Overview Implement the core operational build request status page with real-time updates, job/partition status visualization, and execution logs. ## Scope ### In Scope - Build request status display with real-time updates - Job and partition status visualization - Execution timeline and logs from build events - Delegation indicators for shared builds - Auto-refresh with Page Visibility API - Expandable job details ### Out of Scope - Complex graph visualizations (simple status indicators) - Historical build comparisons - Advanced filtering of events - User interactions beyond viewing ## Technical Approach ### Data Sources From Build Graph Service API: - `/api/v1/builds/:id` - Individual build request details and events - `/api/v1/partitions/:ref/status` - Individual partition status - `/api/v1/partitions/:ref/events` - Partition-specific events Available TypeScript types from generated client: - `BuildStatusResponse` - Build request metadata, status, events - `PartitionStatusResponse` - Individual partition status - `PartitionEventsResponse` - Partition build events - `BuildRequestStatus` enum - Status values (Received, Planning, Executing, Completed, Failed, Cancelled) - `PartitionStatus` enum - Status values (Requested, Scheduled, Building, Available, Failed, Delegated) **Note**: Timestamps are in nanoseconds and require conversion via `/1000000` for JavaScript Date objects. ### Component Structure ```typescript import { DefaultApi, Configuration, BuildStatusResponse, PartitionStatusResponse } from '../client/typescript_generated/src/index'; import { pollingManager, formatTime } from './services'; const BuildStatus = { data: null as BuildStatusResponse | null, loading: true, error: null as string | null, partitionStatuses: new Map(), logsExpanded: {} as Record, oninit: (vnode) => { this.buildId = vnode.attrs.id; this.loadBuild(); this.startPolling(); }, onremove: () => { pollingManager.stopPolling(`build-status-${this.buildId}`); }, async loadBuild() { try { this.loading = true; this.error = null; m.redraw(); const apiClient = new DefaultApi(new Configuration({ basePath: '' })); // Get build status const buildResponse = await apiClient.apiV1BuildsBuildRequestIdGet({ buildRequestId: this.buildId }); this.data = buildResponse; // Load partition statuses for all requested partitions if (buildResponse.requestedPartitions) { for (const partition of buildResponse.requestedPartitions) { try { const partitionStatus = await apiClient.apiV1PartitionsRefStatusGet({ ref: partition.str }); this.partitionStatuses.set(partition.str, partitionStatus); } catch (e) { console.warn(`Failed to load status for partition ${partition.str}:`, e); } } } this.loading = false; m.redraw(); } catch (error) { console.error('Failed to load build:', error); this.error = error instanceof Error ? error.message : 'Failed to load build'; this.loading = false; m.redraw(); } }, startPolling() { // Use different poll intervals based on build status const isActive = this.data?.status === 'BuildRequestExecuting' || this.data?.status === 'BuildRequestPlanning'; const interval = isActive ? 2000 : 10000; // 2s for active, 10s for completed pollingManager.startPolling(`build-status-${this.buildId}`, () => { this.loadBuild(); }, interval); }, view: () => [ // Loading/error states similar to RecentActivity component this.loading && !this.data ? m('.loading-state', '...') : null, this.error ? m('.error-state', this.error) : null, this.data ? [ m('.build-header', [ m('h1', `Build ${this.buildId}`), m('.build-meta', [ m(`span.badge.${this.getStatusClass(this.data.status)}`, this.data.status), m('.timestamp', formatTime(new Date(this.data.createdAt / 1000000).toISOString())), m('.partitions', `${this.data.requestedPartitions?.length || 0} partitions`), ]) ]), m('.build-content', [ m('.partition-status', [ m('h2', 'Partition Status'), m('.partition-grid', this.data.requestedPartitions?.map(partition => { const status = this.partitionStatuses.get(partition.str); return m('.partition-card', [ m('.partition-ref', partition.str), m(`span.badge.${this.getPartitionStatusClass(status?.status)}`, status?.status || 'Unknown'), status?.updatedAt ? m('.updated-time', formatTime(new Date(status.updatedAt / 1000000).toISOString())) : null ]); }) || [] ) ]), m('.execution-timeline', [ m('h2', 'Execution Timeline'), m('.timeline', this.data.events?.map(event => m('.timeline-item', [ m('.timestamp', formatTime(new Date(event.timestamp / 1000000).toISOString())), m('.event-type', event.eventType), m('.message', event.message || ''), // Add expandable logs for job events this.isJobEvent(event) ? m('.expandable-logs', [ m('button.btn.btn-sm', { onclick: () => this.toggleLogs(event.eventId) }, this.logsExpanded[event.eventId] ? 'Hide Logs' : 'Show Logs'), this.logsExpanded[event.eventId] ? m('.logs', this.formatJobLogs(event)) : null ]) : null ]) ) || []) ]) ]) ] : null ] }; ``` ### Real-time Updates - Use existing `pollingManager` from `services.ts` with Page Visibility API - Poll every 2 seconds when build status is `BuildRequestExecuting` or `BuildRequestPlanning` - Poll every 10 seconds when build is `BuildRequestCompleted`, `BuildRequestFailed`, or `BuildRequestCancelled` - Automatic polling pause when tab is not visible - Explicit `m.redraw()` calls after async data loading ### Status Visualization - Color-coded status badges using DaisyUI classes: - `badge-success` for `BuildRequestCompleted` / `PartitionAvailable` - `badge-warning` for `BuildRequestExecuting` / `PartitionBuilding` - `badge-error` for `BuildRequestFailed` / `PartitionFailed` - `badge-neutral` for other states - Timeline visualization for build events with timestamp formatting - Delegation indicators for `PartitionDelegated` status with build request links ## Implementation Strategy 1. **Extend Existing Infrastructure** - Use established `DefaultApi` client pattern from `services.ts` - Leverage existing `pollingManager` with Page Visibility API - Follow `RecentActivity` component patterns for loading/error states - Import generated TypeScript types from client 2. **Build Status Components** - Build header with metadata using `BuildStatusResponse` - Partition status grid using `PartitionStatusResponse` for each partition - Execution timeline parsing `events` array from build response - Expandable log sections for job events with state management 3. **Real-time Updates** - Intelligent polling based on `BuildRequestStatus` enum values - Reuse existing `pollingManager` infrastructure - Loading states and error handling following established patterns - Proper `m.redraw()` calls after async operations 4. **Status Visualization** - Status badge classes using established DaisyUI patterns - Timeline layout similar to existing dashboard components - Partition delegation links using build request IDs - Timestamp formatting using existing `formatTime` utility ## Deliverables - [ ] Build request status page with real-time updates - [ ] Partition status grid with visual indicators - [ ] Execution timeline with build events - [ ] Expandable job logs and details - [ ] Auto-refresh with visibility detection - [ ] Delegation indicators and links ## Success Criteria - Real-time updates show build progress accurately - All partition statuses are clearly displayed - Job logs are accessible and readable - Polling behaves correctly based on build state - Delegation to other builds is clearly indicated - Page is responsive and performs well ## Testing - Test with running builds to verify real-time updates - Verify partition status changes are reflected - Test job log expansion and readability - Validate polling behavior with tab visibility - Test with delegated builds - Verify error handling with invalid build IDs - Check performance with large build requests