Files
cve-dashboard/.kiro/specs/compliance-remediation-display-fix/tasks.md
Jordan Ramos a61d254ff9 Sync .kiro/ from master — v2.2.0 release batch
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
2026-06-04 11:27:31 -06:00

8.4 KiB

Implementation Plan: Compliance Remediation Display Fix

Overview

Fix the compliance main table to display Resolution Date and Remediation Plan values. The GET /items endpoint omits ci.resolution_date and ci.remediation_plan from its SELECT clause, and groupByHostname() does not propagate these fields into device objects. The frontend already renders them correctly when present. This is a backend-only fix: add the columns to the SELECT and update the grouping helper to aggregate them as first-non-null across each hostname's metric rows.

Tasks

  • 1. Write bug condition exploration test

    • Property 1: Bug Condition - Resolution Date and Remediation Plan Missing from GET /items Response
    • CRITICAL: This test MUST FAIL on unfixed code - failure confirms the bug exists
    • DO NOT attempt to fix the test or the code when it fails
    • NOTE: This test encodes the expected behavior - it will validate the fix when it passes after implementation
    • GOAL: Surface counterexamples that demonstrate the bug exists (resolution_date and remediation_plan are undefined in grouped device objects)
    • Scoped PBT Approach: Generate compliance_items rows where resolution_date and/or remediation_plan are non-null, pass them through groupByHostname(), and assert the output device objects contain those fields
    • Bug Condition: isBugCondition(row) = row.resolution_date != null OR row.remediation_plan != null — any row with metadata set will lose it through groupByHostname()
    • Test file: backend/__tests__/compliance-remediation-display-fix.exploration.property.test.js
    • Test structure:
      • Use fast-check to generate arbitrary hostname strings, date strings for resolution_date, and non-empty strings for remediation_plan
      • Construct rows array with generated values present in resolution_date and remediation_plan columns
      • Call groupByHostname() with those rows
      • Assert: device.resolution_date equals the first non-null resolution_date across the hostname's rows
      • Assert: device.remediation_plan equals the first non-null remediation_plan across the hostname's rows
    • Run test on UNFIXED code
    • EXPECTED OUTCOME: Test FAILS because groupByHostname() does not propagate resolution_date or remediation_plan (they are undefined on the output objects)
    • Document counterexamples found (e.g., "groupByHostname([{hostname:'H1', resolution_date:'2025-06-01', ...}]) returns device with resolution_date === undefined")
    • Mark task complete when test is written, run, and failure is documented
    • Requirements: 1.1, 1.2, 2.2
  • 2. Write preservation property tests (BEFORE implementing fix)

    • Property 2: Preservation - Existing groupByHostname Fields Unchanged
    • IMPORTANT: Follow observation-first methodology
    • Test file: backend/__tests__/compliance-remediation-display-fix.preservation.property.test.js
    • Observe: groupByHostname() correctly aggregates hostname, ip_address, device_type, team, status, failing_metrics, seen_count, first_seen, last_seen, resolved_on, has_notes on unfixed code
    • Observe: groupByHostname() deduplicates metrics by metric_id (no duplicate failing_metrics entries)
    • Observe: groupByHostname() picks the maximum seen_count across rows for the same hostname
    • Observe: groupByHostname() picks the earliest first_seen and latest last_seen across rows
    • Test structure:
      • Use fast-check to generate arrays of compliance rows with varying hostnames, metric_ids, seen_counts, first_seen/last_seen dates
      • All generated rows have resolution_date = null and remediation_plan = null (non-bug-condition inputs)
      • Call groupByHostname() and assert:
        • Property: each device.hostname appears exactly once in output
        • Property: device.failing_metrics contains no duplicate metric_ids
        • Property: device.seen_count >= every row's seen_count for that hostname
        • Property: device.first_seen <= every row's first_seen for that hostname
        • Property: device.last_seen >= every row's last_seen for that hostname
        • Property: device.has_notes matches noteHostnames membership
    • Verify test passes on UNFIXED code
    • EXPECTED OUTCOME: Tests PASS (confirms baseline behavior to preserve)
    • Mark task complete when tests are written, run, and passing on unfixed code
    • Requirements: 3.3, 3.5
  • 3. Fix for resolution_date and remediation_plan missing from compliance list endpoint

    • 3.1 Add resolution_date and remediation_plan to the GET /items SQL SELECT clause

      • In backend/routes/compliance.js around line 601, add ci.resolution_date, ci.remediation_plan to the SELECT column list
      • The columns already exist in the compliance_items table — they just need to be selected
      • Bug_Condition: isBugCondition(row) = row.resolution_date != null OR row.remediation_plan != null — these columns are not selected so they are always absent
      • Expected_Behavior: The query result rows include resolution_date and remediation_plan values from the database
      • Preservation: All other selected columns remain unchanged; query WHERE/ORDER BY clauses unchanged
      • Requirements: 1.1, 2.1
    • 3.2 Update groupByHostname() to propagate resolution_date and remediation_plan

      • In backend/routes/compliance.js around line 220, add resolution_date: null, remediation_plan: null to the initial device object
      • In the row iteration loop, aggregate as first-non-null: if (row.resolution_date && !dev.resolution_date) dev.resolution_date = row.resolution_date;
      • Same for remediation_plan: if (row.remediation_plan && !dev.remediation_plan) dev.remediation_plan = row.remediation_plan;
      • This matches the "first non-null value across the hostname's metric rows" aggregation strategy from the requirements
      • Bug_Condition: isBugCondition(device) = device has rows with non-null resolution_date or remediation_plan but groupByHostname() drops them
      • Expected_Behavior: device.resolution_date = first non-null resolution_date across hostname's rows (or null if all null); same for remediation_plan
      • Preservation: All other device fields (hostname, ip_address, device_type, team, status, failing_metrics, seen_count, first_seen, last_seen, resolved_on, has_notes) remain unchanged
      • Requirements: 1.2, 2.2, 3.3, 3.5, 3.6
    • 3.3 Verify bug condition exploration test now passes

      • Property 1: Expected Behavior - Resolution Date and Remediation Plan Present in GET /items Response
      • IMPORTANT: Re-run the SAME test from task 1 — do NOT write a new test
      • The test from task 1 encodes the expected behavior (resolution_date and remediation_plan propagated through groupByHostname)
      • When this test passes, it confirms the expected behavior is satisfied
      • Run: npx jest backend/__tests__/compliance-remediation-display-fix.exploration.property.test.js --run
      • EXPECTED OUTCOME: Test PASSES (confirms bug is fixed)
      • Requirements: 2.1, 2.2, 2.3, 2.4
    • 3.4 Verify preservation tests still pass

      • Property 2: Preservation - Existing groupByHostname Fields Unchanged
      • IMPORTANT: Re-run the SAME tests from task 2 — do NOT write new tests
      • Run: npx jest backend/__tests__/compliance-remediation-display-fix.preservation.property.test.js --run
      • EXPECTED OUTCOME: Tests PASS (confirms no regressions — existing field aggregation unchanged)
      • Confirm all tests still pass after fix (no regressions)
  • 4. Checkpoint - Ensure all tests pass

    • Run full test suite: npx jest backend/__tests__/compliance-remediation-display-fix --run
    • Verify no other compliance tests regressed: npx jest backend/__tests__/compliance --run
    • Ensure all tests pass, ask the user if questions arise.

Task Dependency Graph

{
  "waves": [
    { "id": 0, "tasks": ["1", "2"] },
    { "id": 1, "tasks": ["3.1", "3.2"] },
    { "id": 2, "tasks": ["3.3", "3.4"] },
    { "id": 3, "tasks": ["4"] }
  ]
}

Notes

  • This is a backend-only fix. The frontend (CompliancePage.js lines 883-890) already renders resolution_date and remediation_plan correctly when the data is present.
  • The groupByHostname() function is a pure helper (no DB access), making it ideal for direct property-based testing without HTTP mocking.
  • The "first non-null" aggregation strategy handles per-metric metadata scoping: different metrics on the same hostname may have different resolution_date values, but the list view shows the first encountered non-null for display purposes.