# 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:**
```sql
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`:
```sql
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:**
```sql
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:**
```sql
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:**
```sql
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:**
```sql
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:**
```sql
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:
```sql
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`
```sql
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:
```sql
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'`:
```sql
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.*
