Update next plan
Some checks are pending
/ setup (push) Waiting to run

This commit is contained in:
soaxelbrooke 2025-07-12 11:01:04 -07:00
parent b314ee6abd
commit ebcbe3e928

View file

@ -27,34 +27,101 @@ Implement the core operational build request status page with real-time updates,
## Technical Approach
### Data Sources
From Build Graph Service API `/api/v1/builds/:id`:
- Build request metadata (ID, status, timestamps)
- Requested partitions
- Build events with job execution details
- Delegation information
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<string, PartitionStatusResponse>(),
logsExpanded: {} as Record<string, boolean>,
oninit: (vnode) => {
this.buildId = vnode.attrs.id;
this.build = null;
this.pollInterval = null;
this.loadBuild();
this.startPolling();
},
onremove: () => {
this.stopPolling();
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('.badge', this.build?.status),
m('.timestamp', formatTime(this.build?.created_at)),
m('.partitions', `${this.build?.requested_partitions.length} partitions`),
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`),
])
]),
@ -62,71 +129,84 @@ const BuildStatus = {
m('.partition-status', [
m('h2', 'Partition Status'),
m('.partition-grid',
this.build?.requested_partitions.map(partition =>
m('.partition-card', [
m('.partition-ref', partition),
m('.partition-status', this.getPartitionStatus(partition)),
m('.partition-job', this.getPartitionJob(partition)),
])
)
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.build?.events.map(event =>
m('.timeline', this.data.events?.map(event =>
m('.timeline-item', [
m('.timestamp', formatTime(event.timestamp)),
m('.event-type', event.event_type),
m('.message', event.message),
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', { onclick: () => this.toggleLogs(event) }, 'Show Logs'),
this.logsExpanded[event.event_id] ?
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
- Poll every 2 seconds when build is active
- Poll every 10 seconds when build is completed
- Stop polling when tab is not visible
- Visual indicators for live 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 (green/yellow/red)
- Progress indicators for running builds
- Delegation indicators with links to other builds
- Timeline visualization for build events
- 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. **Create Data Layer**
- API integration for build status
- Event parsing and categorization
- Polling manager with different intervals
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
- Partition status grid
- Execution timeline
- Expandable log sections
- 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 build state
- Page Visibility API integration
- Loading states and error handling
- 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**
- Color-coded status indicators
- Progress bars for active builds
- Timeline layout for events
- Delegation indicators
- 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