12 KiB
Time-Based Reporting Recommendations
Date: 2026-04-02 Author: Engineering (Claude Code) Status: Draft — for director review
Executive Summary
This document analyzes the current CVE Dashboard data model and recommends a set of time-based visualizations that can be added to the Reporting page. Recommendations are grouped by feasibility: Tier 1 can be built with data already in the database, Tier 2 requires a lightweight new tracking table, and Tier 3 requires structural additions.
Current Data Inventory
What Already Has Time-Series History
| Source | Table | Date Fields | History? |
|---|---|---|---|
| Compliance uploads | compliance_uploads |
report_date, uploaded_at |
Yes — one row per report cycle |
| Compliance items | compliance_items |
created_at, first_seen_upload_id, resolved_upload_id |
Yes — tracks lifecycle |
| Archer tickets | archer_tickets |
created_at, updated_at |
Yes — full history |
| Todo queue | ivanti_todo_queue |
created_at, updated_at |
Yes — by action |
| Finding notes | ivanti_finding_notes |
updated_at |
Yes — note activity |
What Is Point-in-Time Only (no history yet)
| Source | Table | Problem |
|---|---|---|
| Ivanti findings | ivanti_findings_cache |
Single-row cache — overwritten on every sync |
| Ivanti counts | ivanti_counts_cache |
Single-row cache — no snapshots stored |
| FP workflow states | Computed from findings_json |
Ephemeral — not persisted historically |
Tier 1 Recommendations — Build Now (No Schema Changes)
All of these use data that is already in the database.
1.1 Compliance Trend Line — Total Active Findings Over Time
Description: A line chart showing the total number of active (non-compliant) items per compliance upload date. This directly answers "are we improving over time?"
Data Source:
SELECT
cu.report_date,
COUNT(ci.id) AS active_count
FROM compliance_uploads cu
JOIN compliance_items ci ON ci.upload_id = cu.id AND ci.status = 'active'
GROUP BY cu.id
ORDER BY cu.report_date ASC;
Chart Type: Line chart with data points per upload Axes: X = Report Date, Y = Number of Active Findings Value-add: Overlay a trend line (linear regression) to show trajectory
1.2 New / Recurring / Resolved Bar Chart — Per Report Cycle
Description: A grouped or stacked bar chart showing the delta breakdown for each compliance upload: how many findings were newly introduced, how many recurred from a prior cycle, and how many were resolved.
Data Source: Already computed and stored in compliance_uploads:
SELECT report_date, new_count, recurring_count, resolved_count
FROM compliance_uploads
ORDER BY report_date ASC;
Chart Type: Stacked bar chart (one bar per upload date) Legend: New (red/amber), Recurring (yellow), Resolved (green) Value-add: Shows whether each reporting cycle is improving (more resolved than new) or degrading
1.3 Team Compliance Health Over Time — Multi-Line Chart
Description: A multi-line chart showing the active finding count per team per upload date. Answers "which team is trending better or worse?"
Data Source:
SELECT
cu.report_date,
ci.team,
COUNT(ci.id) AS active_count
FROM compliance_uploads cu
JOIN compliance_items ci ON ci.upload_id = cu.id AND ci.status = 'active'
GROUP BY cu.id, ci.team
ORDER BY cu.report_date ASC;
Chart Type: Multi-line chart (one line per team) Teams: STEAM, ACCESS-ENG, ACCESS-OPS, INTELDEV Value-add: Immediately visible which team is outlier or improving fastest
1.4 Mean Time to Resolution (MTTR) — Per Team
Description: A bar chart showing average number of upload cycles between when a finding first appeared and when it was resolved, broken out by team.
Data Source:
SELECT
ci.team,
AVG(ci.resolved_upload_id - ci.first_seen_upload_id) AS avg_cycles_to_resolve,
COUNT(*) AS resolved_count
FROM compliance_items ci
WHERE ci.resolved_upload_id IS NOT NULL
GROUP BY ci.team;
Chart Type: Horizontal bar chart Axes: Y = Team, X = Average Cycles to Resolution Value-add: Normalize to calendar days by joining with upload dates for true MTTR in days
1.5 Recurring Findings Heatmap — Seen Count Distribution
Description: A heatmap or bubble chart showing findings grouped by how many times they have recurred (seen_count). Identifies chronic, long-standing compliance gaps.
Data Source:
SELECT
team,
metric_id,
metric_desc,
seen_count,
COUNT(*) AS host_count
FROM compliance_items
WHERE status = 'active'
GROUP BY team, metric_id
ORDER BY seen_count DESC;
Chart Type: Horizontal bar chart sorted by seen_count, grouped by team
Value-add: Highlights the "chronic" findings that repeatedly appear — high priority for remediation
1.6 Archer Exception Ticket Status Over Time
Description: A line chart or cumulative area chart showing Archer ticket status transitions over time using created_at and updated_at.
Data Source:
SELECT
DATE(created_at) AS date,
status,
COUNT(*) AS count
FROM archer_tickets
GROUP BY DATE(created_at), status
ORDER BY date ASC;
Chart Type: Stacked area chart Statuses: Draft, Open, Under Review, Accepted Value-add: Tracks exception request pipeline velocity — are exceptions getting processed or stacking up?
1.7 Compliance Category Breakdown Over Time
Description: A stacked area chart showing what categories of compliance failures are driving the total over time (if the category field in compliance_items is populated).
Data Source:
SELECT
cu.report_date,
ci.category,
COUNT(ci.id) AS count
FROM compliance_uploads cu
JOIN compliance_items ci ON ci.upload_id = cu.id AND ci.status = 'active'
WHERE ci.category IS NOT NULL
GROUP BY cu.id, ci.category
ORDER BY cu.report_date ASC;
Chart Type: Stacked area chart Value-add: Shows whether one category dominates or if failures are spread across areas
Tier 2 Recommendations — Lightweight Schema Addition Required
These require adding one new table to persist snapshots of data that is currently overwritten on each sync.
2.1 Ivanti Findings Count Over Time — Open vs Closed Trend
Description: The single most-requested metric: "are we making progress on vulnerabilities?" A line chart showing open and closed Ivanti finding counts over time.
Problem: The current ivanti_counts_cache is a single-row table overwritten on each sync. No history is kept.
Solution: Add a ivanti_counts_history table and append a row on every successful sync:
CREATE TABLE ivanti_counts_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
open_count INTEGER NOT NULL,
closed_count INTEGER NOT NULL,
recorded_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
Backend change: In the sync route (POST /api/ivanti/findings/sync), after updating the cache, also INSERT INTO ivanti_counts_history.
New API endpoint: GET /api/ivanti/findings/counts/history
SELECT open_count, closed_count, recorded_at
FROM ivanti_counts_history
ORDER BY recorded_at ASC;
Chart Type: Dual-line chart Lines: Open findings (red), Closed findings (green) Value-add: Most direct measure of vulnerability remediation velocity
2.2 FP Workflow State Snapshots Over Time
Description: A stacked area or line chart showing how FP workflow states (Actionable, Requested, Approved, Rejected, Expired) trend over sync cycles.
Solution: Add a ivanti_fp_workflow_history table:
CREATE TABLE ivanti_fp_workflow_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
state TEXT NOT NULL,
finding_count INTEGER NOT NULL DEFAULT 0,
id_count INTEGER NOT NULL DEFAULT 0,
recorded_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
Chart Type: Stacked area chart Value-add: Shows whether FP requests are being worked through or stacking up in "Requested" state
2.3 Todo Queue Velocity — Items Added vs Completed Per Week
Description: A bar chart showing weekly queue throughput (items added vs items marked complete).
Data Source: Already available in ivanti_todo_queue.created_at and updated_at + status = 'complete':
SELECT
STRFTIME('%Y-W%W', created_at) AS week,
COUNT(*) AS items_added,
SUM(CASE WHEN status = 'complete' THEN 1 ELSE 0 END) AS items_completed
FROM ivanti_todo_queue
GROUP BY week
ORDER BY week ASC;
Chart Type: Grouped bar chart (weekly) Value-add: Measures operational pace of the team's workflow action throughput
Tier 3 Recommendations — Structural Additions (Future Consideration)
These require more significant changes but would provide powerful long-term reporting.
3.1 Finding Age / Dwell Time Distribution
Description: A histogram showing how long open findings have been open (age in days). The lastFoundOn field exists in the Ivanti findings JSON but is not persisted to a structured table.
Requirement: Parse and store lastFoundOn from findings JSON into a structured column during sync.
Value-add: Highlights findings that have been open for 90+ days — high-priority remediation targets.
3.2 SLA Breach Trends
Description: Track how many findings breach SLA (Due Date exceeded) over time. Currently SLA status is computed in the frontend on-the-fly.
Requirement: Add SLA breach tracking during sync — stamp findings that cross SLA date.
Value-add: Compliance and audit reporting for SLA adherence metrics.
Recommended Implementation Order
| Priority | Chart | Effort | Impact |
|---|---|---|---|
| 1 | 1.2 — New/Recurring/Resolved bar chart | Low (data ready) | High |
| 2 | 1.1 — Compliance trend line | Low (data ready) | High |
| 3 | 1.3 — Team health multi-line | Low (data ready) | High |
| 4 | 2.1 — Ivanti open/closed history | Medium (new table) | Very High |
| 5 | 1.4 — MTTR per team | Low (data ready) | Medium |
| 6 | 1.6 — Archer ticket pipeline | Low (data ready) | Medium |
| 7 | 2.3 — Queue velocity | Low (data ready) | Medium |
| 8 | 1.5 — Recurring findings heatmap | Low (data ready) | Medium |
| 9 | 2.2 — FP workflow snapshots | Medium (new table) | Medium |
| 10 | 1.7 — Category breakdown | Low (data ready) | Low–Medium |
Charting Library Consideration
The current implementation uses hand-rolled SVG donut charts (no external library). For time-series line/bar/area charts, the team should decide:
| Option | Pros | Cons |
|---|---|---|
| Continue hand-rolled SVG | Zero dependencies, full style control | Significant effort for axes, labels, tooltips |
| Recharts (React-native) | Well-matched to React 19, composable, responsive | ~500KB dependency |
| Chart.js via react-chartjs-2 | Mature, widely documented | Less React-idiomatic |
| Lightweight: uPlot or Chart.xkcd | Very small bundle | Less community support |
Recommendation: Recharts aligns best with the React 19 stack and allows declaring charts as JSX components consistent with the existing code style. It supports all chart types listed above.
Notes for Director Review
- All Tier 1 recommendations can be implemented with zero database migrations — the data is already there.
- The single highest-value addition is
2.1 — Ivanti open/closed count history, as it captures the most direct remediation progress metric. It only requires one new table and one line added to the sync handler. - Compliance charts (1.1–1.5) will only be meaningful once multiple compliance uploads have been committed. If only 1–2 uploads exist currently, the trend will not show much until more data accumulates — but building the charts now means data will automatically populate them.
- All queries listed above have been validated against the actual database schema.
Next step: Review with director, confirm priority order, then schedule sprint for implementation.