New specs: archer-template-library, ccp-metrics-view-restructure, compliance-list-stale-after-sidebar-edit, compliance-metric-estimated-resolution-date, compliance-remediation-display-fix, flexible-jira-ticket-creation, forecast-burndown-chart, granite-loader-export, ivanti-queue-clear-completed-fix, multi-item-jira-ticket, queue-collapsible-sections, vendor-issue-type-dropdown New steering: archer-template-gen.md Updated: migration-registration-check hook, remediation-plan-history spec, gitlab-workflow, tech, versioning steering files
159 lines
9.1 KiB
Markdown
159 lines
9.1 KiB
Markdown
# Implementation Plan: Forecast Burndown Chart
|
|
|
|
## Overview
|
|
|
|
Add a per-metric forecast burndown chart to the CCP Metrics page. A pure helper function (`computeMetricForecastBurndown`) computes forecast projections from device records and historical snapshots. Two new API endpoints serve the metrics list and forecast data. A React frontend renders a metric selector and a ComposedChart (Bar + Line + ReferenceLine) using recharts. No database migrations are needed — the feature reads from existing `compliance_items` and `compliance_snapshots` tables.
|
|
|
|
## Tasks
|
|
|
|
- [x] 1. Implement the computeMetricForecastBurndown helper function
|
|
- [x] 1.1 Add computeMetricForecastBurndown to backend/helpers/vclHelpers.js
|
|
- Implement the pure function accepting `currentDevices`, `totalAssets`, and `historicalSnapshots`
|
|
- Return object with `historical`, `forecast`, and `current_snapshot` fields
|
|
- Compute `current_snapshot.blockers` (devices with no resolution_date) and `current_snapshot.with_dates` (devices with a resolution_date)
|
|
- Compute `compliance_pct` as `ROUND((total_assets - non_compliant) / total_assets * 100, 1)`, returning 0 when totalAssets is 0
|
|
- Generate forecast months by iterating forward from current month, decrementing non_compliant as devices reach their resolution_date month
|
|
- Treat past-due resolution dates as remediated in the current month
|
|
- Hold total_assets constant across all forecast data points
|
|
- Terminate forecast when all dated devices are remediated or at 12-month maximum
|
|
- Return empty forecast array when all devices are blockers (no resolution dates)
|
|
- Export the function for use in route handlers and tests
|
|
- _Requirements: 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10, 3.11, 6.5_
|
|
|
|
- [ ]* 1.2 Write property test: Forecast structure invariant
|
|
- **Property 1: Forecast structure invariant**
|
|
- **Validates: Requirements 1.4, 3.1, 3.6**
|
|
- Test file: `backend/__tests__/forecast-burndown-chart.property.test.js`
|
|
|
|
- [ ]* 1.3 Write property test: Blocker and with_dates partition invariant
|
|
- **Property 2: Blocker and with_dates partition invariant**
|
|
- **Validates: Requirements 3.2**
|
|
- Test file: `backend/__tests__/forecast-burndown-chart.property.test.js`
|
|
|
|
- [ ]* 1.4 Write property test: Compliance percentage formula correctness
|
|
- **Property 3: Compliance percentage formula correctness**
|
|
- **Validates: Requirements 3.3, 3.10**
|
|
- Test file: `backend/__tests__/forecast-burndown-chart.property.test.js`
|
|
|
|
- [ ]* 1.5 Write property test: Forecast non_compliant monotonicity
|
|
- **Property 4: Forecast non_compliant monotonicity**
|
|
- **Validates: Requirements 3.4**
|
|
- Test file: `backend/__tests__/forecast-burndown-chart.property.test.js`
|
|
|
|
- [ ]* 1.6 Write property test: Per-month non_compliant computation correctness
|
|
- **Property 5: Per-month non_compliant computation correctness**
|
|
- **Validates: Requirements 3.8**
|
|
- Test file: `backend/__tests__/forecast-burndown-chart.property.test.js`
|
|
|
|
- [ ]* 1.7 Write property test: Forecast horizon bound
|
|
- **Property 6: Forecast horizon bound**
|
|
- **Validates: Requirements 1.9, 3.9**
|
|
- Test file: `backend/__tests__/forecast-burndown-chart.property.test.js`
|
|
|
|
- [ ]* 1.8 Write property test: Past-due resolution dates treated as current month
|
|
- **Property 7: Past-due resolution dates treated as current month**
|
|
- **Validates: Requirements 6.5**
|
|
- Test file: `backend/__tests__/forecast-burndown-chart.property.test.js`
|
|
|
|
- [x] 2. Checkpoint - Helper function verified
|
|
- Ensure all tests pass, ask the user if questions arise.
|
|
|
|
- [x] 3. Implement backend API endpoints
|
|
- [x] 3.1 Add GET /metrics-list endpoint to backend/routes/vclMultiVertical.js
|
|
- Add route handler at `/metrics-list` with `requireAuth()` middleware
|
|
- Query `compliance_items` for distinct metric_ids with active non-compliant devices where vertical IS NOT NULL
|
|
- Return JSON array of `{ metric_id, device_count }` sorted by metric_id ascending
|
|
- Return empty array when no metrics have active devices
|
|
- Return HTTP 500 with `{ "error": "Failed to fetch metrics list" }` on database failure
|
|
- _Requirements: 2.1, 2.2, 2.3, 2.4, 2.5, 2.6_
|
|
|
|
- [x] 3.2 Add GET /metric/:metricId/forecast-burndown endpoint to backend/routes/vclMultiVertical.js
|
|
- Add route handler with `requireAuth()` middleware
|
|
- Query active devices for the metric from `compliance_items` (status = 'active', vertical IS NOT NULL)
|
|
- Determine the vertical from active devices and query `compliance_snapshots` for 3 months of historical data
|
|
- Compute per-metric historical non_compliant using the ratio method from Requirement 7.2
|
|
- Include current month as the most recent historical data point computed from live data
|
|
- Pass data to `computeMetricForecastBurndown` helper
|
|
- Return response with `metric_id`, `historical`, `forecast`, and `current_snapshot`
|
|
- Return 200 with empty arrays and zeroed snapshot when metricId has no active devices
|
|
- Return HTTP 500 with `{ "error": "Failed to compute forecast burndown" }` on database failure
|
|
- _Requirements: 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 1.10, 6.1, 6.2, 6.3, 6.4, 6.5, 6.6, 7.1, 7.2, 7.3, 7.4, 7.5, 7.6, 7.7_
|
|
|
|
- [ ]* 3.3 Write unit tests for API endpoints
|
|
- Test metrics-list returns correct shape with mocked database
|
|
- Test forecast-burndown returns correct shape with mocked database
|
|
- Test authentication middleware is applied to both endpoints
|
|
- Test empty/error states
|
|
- Test file: `backend/__tests__/forecast-burndown-chart.test.js`
|
|
- _Requirements: 1.7, 1.8, 1.10, 2.4, 2.5, 2.6_
|
|
|
|
- [x] 4. Checkpoint - Backend complete
|
|
- Ensure all tests pass, ask the user if questions arise.
|
|
|
|
- [x] 5. Implement frontend MetricSelector and ForecastBurndownChart components
|
|
- [x] 5.1 Add MetricSelector component to frontend/src/components/pages/CCPMetricsPage.js
|
|
- Fetch metrics list from `/api/compliance/vcl-multi/metrics-list` on mount
|
|
- Display dropdown showing each metric_id with active non-compliant device count
|
|
- Auto-select first metric and trigger forecast data fetch on load
|
|
- Handle loading state (non-interactive while fetching)
|
|
- Handle empty state ("No metrics with active non-compliant devices")
|
|
- Handle error state (inline error with AlertCircle icon, red border)
|
|
- On selection change, trigger `onMetricSelect` callback
|
|
- _Requirements: 5.1, 5.2, 5.3, 5.4, 5.5, 5.6, 5.7, 5.8_
|
|
|
|
- [x] 5.2 Add ForecastBurndownChart component to frontend/src/components/pages/CCPMetricsPage.js
|
|
- Fetch forecast data from `/api/compliance/vcl-multi/metric/:metricId/forecast-burndown` when metric changes
|
|
- Render recharts ComposedChart with:
|
|
- Blue Bar for total_assets (left Y-axis)
|
|
- Orange Bar for non_compliant (left Y-axis)
|
|
- Green Line for compliance_pct (right Y-axis, 0-100%)
|
|
- ReferenceLine as vertical divider between historical and forecast sections
|
|
- Render forecast data points at 50% opacity
|
|
- Display raw device count labels inside bars
|
|
- Display compliance percentage labels on the trend line
|
|
- X-axis labeled with months (YYYY-MM format)
|
|
- Left Y-axis scaled to max total_assets, right Y-axis 0-100%
|
|
- Handle loading state (loading indicator in chart area)
|
|
- Handle error state (inline error with AlertCircle icon and description)
|
|
- Handle empty data state ("No data available for this metric")
|
|
- Handle historical-only state (no divider line when forecast is empty)
|
|
- Discard stale responses on rapid metric switching (race condition handling)
|
|
- _Requirements: 4.1, 4.2, 4.3, 4.4, 4.5, 4.6, 4.7, 4.8, 4.9, 4.10, 4.11, 4.12, 4.13, 4.14, 5.9_
|
|
|
|
- [x] 5.3 Wire MetricSelector and ForecastBurndownChart into CCPMetricsPage layout
|
|
- Add state for selected metric
|
|
- Place MetricSelector above ForecastBurndownChart in a dedicated section
|
|
- Connect selection change to chart data fetch
|
|
- Follow existing inline-style patterns from AggregatedBurndownChart and TrendChart
|
|
- _Requirements: 4.1, 5.1, 5.4_
|
|
|
|
- [x] 6. Final checkpoint - Ensure all tests pass
|
|
- Ensure all tests pass, ask the user if questions arise.
|
|
|
|
## Notes
|
|
|
|
- Tasks marked with `*` are optional and can be skipped for faster MVP
|
|
- Each task references specific requirements for traceability
|
|
- Checkpoints ensure incremental validation
|
|
- Property tests validate universal correctness properties from the design document
|
|
- The helper function is pure and stateless — all 7 property tests exercise it in isolation without database mocks
|
|
- All backend changes are in `backend/helpers/vclHelpers.js` and `backend/routes/vclMultiVertical.js`
|
|
- All frontend changes are within `frontend/src/components/pages/CCPMetricsPage.js`
|
|
- Property-based tests use `fast-check` (already in project dependencies)
|
|
- No database migrations needed — uses existing `compliance_items` and `compliance_snapshots` tables
|
|
|
|
## Task Dependency Graph
|
|
|
|
```json
|
|
{
|
|
"waves": [
|
|
{ "id": 0, "tasks": ["1.1"] },
|
|
{ "id": 1, "tasks": ["1.2", "1.3", "1.4", "1.5", "1.6", "1.7", "1.8"] },
|
|
{ "id": 2, "tasks": ["3.1", "3.2"] },
|
|
{ "id": 3, "tasks": ["3.3"] },
|
|
{ "id": 4, "tasks": ["5.1", "5.2"] },
|
|
{ "id": 5, "tasks": ["5.3"] }
|
|
]
|
|
}
|
|
```
|