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
5.0 KiB
Implementation Plan: Remediation Plan History — Per-Metric Extension
Overview
Extends the existing remediation plan history system to support per-metric scoping of resolution_date and remediation_plan. The original hostname-level implementation (Tasks 1–6) is already complete. Tasks 7–12 add metric_id targeting to the PATCH endpoint, a metric selector UI in the detail panel, per-metric history tracking, and verification that reporting queries work correctly with per-metric resolution dates. This addresses GitLab issue #19.
Tasks
-
1. Create migration for compliance_item_history table
- 1.1 Create
backend/migrations/add_compliance_item_history.jswith schema, CHECK constraint, indexes, register in run-all.js, and verify table exists- Requirements: 2, 7
- 1.1 Create
-
2. Modify PATCH /items/:hostname/metadata to record history
- 2.1 Accept change_reason, SELECT current values, INSERT history for changed fields, wrap in transaction, handle NULL transitions, add audit log
- Requirements: 1, 6
- 2.1 Accept change_reason, SELECT current values, INSERT history for changed fields, wrap in transaction, handle NULL transitions, add audit log
-
3. Extend GET /items/:hostname to return history
- 3.1 Add history query, include history array in response, graceful degradation on failure
- Requirements: 4
- 3.1 Add history query, include history array in response, graceful degradation on failure
-
4. Modify bulk update commit to track history
- 4.1 Query current values before update, INSERT history for changed fields, skip unchanged, NULL change_reason
- Requirements: 6
- 4.1 Query current values before update, INSERT history for changed fields, skip unchanged, NULL change_reason
-
5. Add change_reason input and history section to ComplianceDetailPanel
- 5.1 Add changeReason state and input, pass in PATCH body, display Change History section with formatted entries, run npm run build
- Requirements: 5
- 5.1 Add changeReason state and input, pass in PATCH body, display Change History section with formatted entries, run npm run build
-
6. Verify VCL burndown is unaffected
- 6.1 Confirm burndown and donut queries read from compliance_items.resolution_date only, no code changes
- Requirements: 3
- 6.1 Confirm burndown and donut queries read from compliance_items.resolution_date only, no code changes
-
7. Create migration to add metric_id column to compliance_item_history
- 7.1 Create
backend/migrations/add_compliance_history_metric_id.jsthat adds nullablemetric_id TEXTcolumn (idempotent check if column exists), creates index on (hostname, metric_id), register in run-all.js, run migration and verify column exists, verify existing rows retain NULL- Requirements: 14
- 7.1 Create
-
8. Extend PATCH /items/:hostname/metadata to support per-metric scoping
- 8.1 Accept optional
metric_id(string) andmetric_ids(array) in request body; if both provided use metric_ids; validate non-empty and max 100 chars; validate each corresponds to active compliance_item- Requirements: 8
- 8.2 When metric_ids provided: SELECT current values per targeted metric, INSERT history with metric_id per changed field, UPDATE only matching rows
- Requirements: 8, 11
- 8.3 When neither metric_id nor metric_ids provided: preserve hostname-level behavior with NULL metric_id in history entries
- Requirements: 15
- 8.1 Accept optional
-
9. Extend GET /items/:hostname to include metric_id in history entries
- 9.1 Update history query to SELECT metric_id column, include metric_id in each history entry in response, verify NULL entries returned correctly
- Requirements: 4, 11
- 9.1 Update history query to SELECT metric_id column, include metric_id in each history entry in response, verify NULL entries returned correctly
-
10. Add MetricChipSelector for metadata editing in ComplianceDetailPanel
- 10.1 Add metricSelection state separate from notes selector, default all active metrics selected on panel open, render MetricChipSelector above resolution_date/remediation_plan inputs with category-colored chips and Select All/Deselect All toggle
- Requirements: 9
- 10.2 Implement computeSharedValues: display shared value when all selected metrics agree, show "Multiple values" placeholder when they differ, omit unchanged fields from PATCH
- Requirements: 10
- 10.3 When all metrics selected omit metric_ids from request body (backward compat), when subset selected include metric_ids, run npm run build
- Requirements: 15
- 10.1 Add metricSelection state separate from notes selector, default all active metrics selected on panel open, render MetricChipSelector above resolution_date/remediation_plan inputs with category-colored chips and Select All/Deselect All toggle
-
11. Update history display to show per-metric labels
- 11.1 When history entry has non-null metric_id display MetricChip, when null display "All metrics" label, build metricMap from metrics array, verify existing entries display correctly, run npm run build
- Requirements: 12
- 11.1 When history entry has non-null metric_id display MetricChip, when null display "All metrics" label, build metricMap from metrics array, verify existing entries display correctly, run npm run build
-
12. Verify per-metric burndown reporting works correctly
- 12.1 Confirm burndown forecast reads resolution_date per compliance_items row, donut uses per-row presence, aggregated view uses MAX(resolution_date), no code changes expected
- Requirements: 13
- 12.1 Confirm burndown forecast reads resolution_date per compliance_items row, donut uses per-row presence, aggregated view uses MAX(resolution_date), no code changes expected
Notes
- Tasks 1–6 are already implemented (hostname-level history tracking is live in production)
- Tasks 7–12 implement the per-metric extension from GitLab issue #19
- Task 7 must run first as it adds the metric_id column needed by all subsequent tasks
- Tasks 10 and 11 can run in parallel after Task 9 completes
- Task 12 is verification-only and depends on both frontend tasks completing
Task Dependency Graph
{
"waves": [
{ "id": 0, "tasks": ["7.1"] },
{ "id": 1, "tasks": ["8.1", "8.2", "8.3"] },
{ "id": 2, "tasks": ["9.1"] },
{ "id": 3, "tasks": ["10.1", "10.2", "10.3", "11.1"] },
{ "id": 4, "tasks": ["12.1"] }
]
}