Files
cve-dashboard/.kiro/specs/compliance-metric-estimated-resolution-date/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

139 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Implementation Plan: Compliance Metric Estimated Resolution Date
## Overview
This plan implements a read-only, per-metric estimated resolution date line at the top of each noncompliant metric's section in the asset sidebar (`ComplianceDetailPanel.js`). The work is frontend-only (React 19, plain JavaScript) and is built test-first: the pure date helper and its property-based tests come first, followed by the `MetricRow` rendering change and its render tests, then build and test verification.
All date logic is isolated in a pure helper (`frontend/src/utils/resolutionDate.js`) so it can be property- and example-tested independently of React. The component change adds a single read-only block to `MetricRow` and changes no prop signatures. The editable Resolution Date metadata `Section`, `computeSharedValues`, `handleSaveMetadata`, and the metadata PATCH flow are left unchanged.
## Tasks
- [x] 1. Create the pure resolution-date helper module
- [x] 1.1 Implement `formatResolutionDate` and display constants in `frontend/src/utils/resolutionDate.js`
- Create `frontend/src/utils/resolutionDate.js` following the existing `frontend/src/utils/` pure-module pattern (for example `queueGrouping.js`)
- Export `formatResolutionDate(raw)` returning the discriminated union `{ state: 'set', value } | { state: 'none' } | { state: 'invalid' }`
- Return `{ state: 'none' }` for `null`, `undefined`, empty string, or whitespace-only (after `trim()`)
- Return `{ state: 'set', value }` only when the trimmed value matches `^\d{4}-\d{2}-\d{2}$` AND is a real calendar date (month `112`, day within the month's true length, leap-year aware); `value` is the normalized `YYYY-MM-DD` string
- Return `{ state: 'invalid' }` for any other non-empty value (wrong shape, `2026-13-01`, `2026-02-30`, arbitrary text)
- Keep the function pure and deterministic: no React, no I/O, no system clock/timezone/locale dependency, and do NOT use `new Date(string)` for the validity decision
- Export `RESOLUTION_DATE_LABEL = 'Est. Resolution'`, `NO_DATE_PLACEHOLDER = 'not set'`, and `INVALID_DATE_PLACEHOLDER = 'invalid date'` as the single source of truth for component and tests
- _Requirements: 1.1, 1.4, 1.6, 2.1_
- [x]* 1.2 Write property test for valid calendar date classification and formatting
- File: `frontend/src/utils/__tests__/resolutionDate.property.test.js`
- Use `fast-check` (v4) with Jest (`react-scripts test`); do not hand-roll generators
- **Property 1: Valid calendar dates classify as "set" and format as YYYY-MM-DD**
- Generator: valid calendar dates spanning years, all months, month-length boundaries (28/29/30/31), and leap days; assert `state === 'set'`, `value` matches `^\d{4}-\d{2}-\d{2}$` and equals the canonical normalized form
- Run a minimum of 100 iterations (`{ numRuns: 100 }` or higher)
- Tag the test: `// Feature: compliance-metric-estimated-resolution-date, Property 1: Valid calendar dates classify as "set" and format as YYYY-MM-DD`
- **Validates: Requirements 1.1, 1.4**
- [x]* 1.3 Write property test for absent values classifying as "none"
- File: `frontend/src/utils/__tests__/resolutionDate.property.test.js`
- **Property 2: Absent values classify as "none"**
- Generator: `fc.constantFrom(null, undefined, '')` combined with whitespace-only strings built from spaces, tabs, and newlines of varying length; assert `state === 'none'`
- Minimum 100 iterations
- Tag the test: `// Feature: compliance-metric-estimated-resolution-date, Property 2: Absent values classify as "none"`
- **Validates: Requirements 2.1, 4.5**
- [x]* 1.4 Write property test for non-calendar-date values classifying as "invalid"
- File: `frontend/src/utils/__tests__/resolutionDate.property.test.js`
- **Property 3: Non-empty non-calendar-date values classify as "invalid"**
- Generator: non-empty, non-whitespace-only strings that are not valid `YYYY-MM-DD` dates (wrong shapes, month `00`/`13`+, day `00`/`32`+, `2026-02-30`, arbitrary text), filtered to exclude any accidentally-valid date; assert `state === 'invalid'`
- Minimum 100 iterations
- Tag the test: `// Feature: compliance-metric-estimated-resolution-date, Property 3: Non-empty non-calendar-date values classify as "invalid"`
- **Validates: Requirements 1.6**
- [x]* 1.5 Write property test for total classification over any metric list
- File: `frontend/src/utils/__tests__/resolutionDate.property.test.js`
- **Property 4: Classification is total over any metric list**
- Generator: arrays mixing all input categories (valid dates, `null`, empty, whitespace-only, malformed); assert `formatResolutionDate` never throws, each result's `state` is in `{ 'set', 'none', 'invalid' }`, and the result count equals the input length
- Minimum 100 iterations
- Tag the test: `// Feature: compliance-metric-estimated-resolution-date, Property 4: Classification is total over any metric list`
- **Validates: Requirements 2.2**
- [x]* 1.6 Write property test for per-metric independence (no collapsing)
- File: `frontend/src/utils/__tests__/resolutionDate.property.test.js`
- **Property 5: Each metric's display derives only from its own field (no collapsing)**
- Generator: arrays of metric-like objects with independently chosen `resolution_date` values (including arrays forced to contain differing dates); assert each metric's derived result deep-equals `formatResolutionDate` applied to that same metric's own `resolution_date` computed in isolation, and that no result is replaced by a shared/"Multiple values" sentinel
- Minimum 100 iterations
- Tag the test: `// Feature: compliance-metric-estimated-resolution-date, Property 5: Each metric's display derives only from its own field (no collapsing)`
- **Validates: Requirements 1.3, 3.2, 3.3**
- [x]* 1.7 Write example and edge-case unit tests for the helper
- File: `frontend/src/utils/__tests__/resolutionDate.test.js`
- Concrete fixtures: `'2026-07-01'``{ state: 'set', value: '2026-07-01' }`; `'2026-7-1'``invalid` (not zero-padded); `'07/01/2026'``invalid`; `'2024-02-29'``set` (leap year); `'2023-02-29'``invalid`; `' '``none`; `null``none`
- _Requirements: 1.1, 1.4, 1.6, 2.1_
- [x] 2. Checkpoint - Ensure helper tests pass
- Run `cd frontend && CI=true npm test -- resolutionDate` to confirm the helper and its property tests pass
- Ensure all tests pass, ask the user if questions arise.
- [x] 3. Render the estimated-resolution-date line in `MetricRow`
- [x] 3.1 Add the read-only date block to `MetricRow` in `frontend/src/components/pages/ComplianceDetailPanel.js`
- Import `formatResolutionDate`, `RESOLUTION_DATE_LABEL`, `NO_DATE_PLACEHOLDER`, and `INVALID_DATE_PLACEHOLDER` from `../../utils/resolutionDate`
- Render the new block as the first child of the row content, above the existing top row (`MetricChip`), the metric description, the Ivanti ID row, and the highlights list (Requirement 1.2)
- Render the block only when `resolved` is falsy; for `resolved === true`, `MetricRow` must behave exactly as today (Requirements 3.1, 3.4)
- For active metrics, call `formatResolutionDate(metric.resolution_date)` and render by state: `set` → label + `YYYY-MM-DD` value; `none` → label + `NO_DATE_PLACEHOLDER`; `invalid` → label + `INVALID_DATE_PLACEHOLDER`, with the rest of the row still rendering
- Render `RESOLUTION_DATE_LABEL` as a visible text label adjacent to the value/placeholder (Requirement 1.5)
- Use plain text only: no `input`, `button`, `a`, or change handler in the new subtree (Requirements 5.3, 5.4)
- Do NOT change the `MetricRow` prop signature; read `resolution_date` from the existing `metric` object
- Leave `computeSharedValues`, `handleSaveMetadata`, the editable Resolution Date metadata `Section`, and the metadata PATCH flow unchanged (Requirement 4.1)
- Prefix any intentionally-unused variables with `_` per the project lint rules
- _Requirements: 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 2.1, 2.2, 3.1, 3.2, 3.3, 3.4, 4.1, 5.3, 5.4_
- [x]* 3.2 Write render tests for placement, labels, and placeholders
- File: `frontend/src/components/pages/__tests__/ComplianceDetailPanel.metricRow.test.js` using `@testing-library/react`
- Placement (1.2): an active row with a valid date renders the estimated-resolution element before the description in document order
- Label presence (1.5): `RESOLUTION_DATE_LABEL` text appears adjacent to the value for an active row with a valid date
- Set value (1.1, 1.4): an active row with `'2026-07-01'` renders `2026-07-01`
- No-date placeholder (2.1, 4.5): active rows with `null`/empty/whitespace render `NO_DATE_PLACEHOLDER` and still render the metric description
- Invalid placeholder (1.6): an active row with a malformed date renders `INVALID_DATE_PLACEHOLDER` and still renders the metric description
- _Requirements: 1.1, 1.2, 1.4, 1.5, 1.6, 2.1, 4.5_
- [x]* 3.3 Write render tests for resolved suppression, read-only structure, and role-independence
- File: `frontend/src/components/pages/__tests__/ComplianceDetailPanel.metricRow.test.js`
- Resolved suppression (3.1, 3.4): a resolved row with a populated date renders no estimated-resolution line; a mixed list shows the line only in active rows
- Read-only structure (5.3): the date-line subtree contains no `input`, `button`, `a`, or change handler
- Role-independence (5.1, 5.2, 5.4): rendering under viewer, editor, and admin auth contexts produces identical date-line output and introduces no editing control
- Existing editor preserved (4.1): the panel still renders the editable Resolution Date `input[type=date]`
- _Requirements: 3.1, 3.4, 4.1, 5.1, 5.2, 5.3, 5.4_
- [x]* 3.4 Write interaction tests for the existing save round-trip
- File: `frontend/src/components/pages/__tests__/ComplianceDetailPanel.metricRow.test.js`
- Successful save (4.2, 4.3, 4.5): mock a successful `PATCH` followed by `fetchDetail` returning updated metrics; assert the displayed value updates to the new date, and that clearing the field renders the no-date placeholder
- Failed save (4.4): mock a failing `PATCH`; assert `detail` is unmodified (previously displayed date retained) and an error indication is shown
- _Requirements: 4.2, 4.3, 4.4, 4.5_
- [ ] 4. Final checkpoint - Build and test verification
- [x] 4.1 Verify production build and ESLint budget
- Run `cd frontend && npm run build` and confirm the build compiles
- Confirm ESLint warnings stay within the 25-warning budget; prefix any intentionally-unused variables with `_`
- _Requirements: 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 2.1, 2.2, 3.1, 3.2, 3.3, 3.4, 4.1_
- [x] 4.2 Run the full test suite
- Run `cd frontend && CI=true npm test` (non-watch) to execute the new property and render tests alongside the existing suite
- Ensure all tests pass, ask the user if questions arise.
## Notes
- Tasks marked with `*` are optional test sub-tasks and can be skipped for a faster MVP, but they validate the five correctness properties and the fixed-DOM acceptance criteria and are recommended.
- Each task references specific requirements (and, for property tests, the design property number) for traceability.
- Test-driven ordering: the pure helper and its property tests (task 1) come before the component change (task 3) so the only branching logic is validated first.
- Property tests use `fast-check` with a minimum of 100 iterations and are tagged with their feature and property number per the design's Testing Strategy.
- Checkpoints (tasks 2 and 4) ensure incremental validation and a clean production build within the lint budget.
## 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", "3.1"] },
{ "id": 2, "tasks": ["3.2", "3.3", "3.4"] },
{ "id": 3, "tasks": ["4.1"] },
{ "id": 4, "tasks": ["4.2"] }
]
}
```