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
This commit is contained in:
Jordan Ramos
2026-06-04 11:27:31 -06:00
parent 8ebd7e4d5e
commit a61d254ff9
54 changed files with 6992 additions and 59 deletions

View File

@@ -0,0 +1,124 @@
# Implementation Plan: Multi-Item Jira Ticket Creation
## Overview
This plan implements multi-select on the Ivanti Queue page and a consolidation modal that creates a single Jira ticket from multiple selected queue items. The implementation proceeds from database migration → backend junction endpoints → frontend aggregation logic → frontend selection UI → consolidation modal → ticket link badges.
## Tasks
- [x] 1. Database migration for junction table
- [x] 1.1 Create migration file `backend/migrations/add_multi_item_jira_ticket.js`
- Verify `jira_tickets` and `ivanti_todo_queue` tables exist; exit with error if missing
- Create `jira_ticket_queue_items` table with columns: id (SERIAL PRIMARY KEY), jira_ticket_id (INTEGER NOT NULL REFERENCES jira_tickets(id)), queue_item_id (INTEGER NOT NULL REFERENCES ivanti_todo_queue(id)), created_at (TIMESTAMPTZ DEFAULT NOW())
- Add UNIQUE constraint on (jira_ticket_id, queue_item_id)
- Add index on queue_item_id
- Add index on jira_ticket_id
- Ensure full idempotency — safe to run multiple times
- _Requirements: 7.1, 7.2, 7.3, 7.4, 7.5_
- [x] 2. Backend junction table endpoints
- [x] 2.1 Add `POST /api/jira-tickets/:id/queue-items` endpoint in `backend/routes/jiraTickets.js`
- Validate `queue_item_ids` is a non-empty array of integers
- Verify the jira_ticket exists
- Verify all referenced queue items exist
- Insert rows into `jira_ticket_queue_items` with ON CONFLICT DO NOTHING
- Return 201 with linked_count
- Require Admin or Standard_User group
- _Requirements: 5.3, 6.1, 6.2_
- [x] 2.2 Add `GET /api/ivanti/todo-queue/ticket-links` endpoint in `backend/routes/ivantiTodoQueue.js`
- Join `jira_ticket_queue_items` with `jira_tickets` to get ticket_key and url
- Filter by queue items belonging to the authenticated user
- Return a map of queue_item_id → { ticket_key, jira_url }
- _Requirements: 6.3, 6.4_
- [x] 3. Frontend aggregation utility functions
- [x] 3.1 Create `frontend/src/utils/jiraConsolidation.js` with pure functions
- `generateConsolidatedSummary(items)` — format: `[N findings] vendor - title`, truncated to 255 chars
- `generateConsolidatedDescription(items)` — structured description grouped by vendor
- `extractFirstCve(items)` — first CVE from first item with non-empty cves_json
- `extractCommonVendor(items)` — common vendor if all same, empty string otherwise
- _Requirements: 3.1, 3.2, 4.1, 4.2, 4.3, 4.4, 4.6, 4.7_
- [ ]* 3.2 Write property tests for aggregation functions (Properties 16)
- **Property 1: Summary format and truncation** — starts with `[N findings]`, at most 255 chars, contains correct vendor label
- **Property 2: Description includes all items** — every item's finding_title appears in output
- **Property 3: Description groups by vendor** — items with same vendor are contiguous
- **Property 4: Description header contains count** — output contains the item count
- **Property 5: First CVE extraction** — returns first CVE from first item with CVEs, or empty string
- **Property 6: Common vendor extraction** — returns vendor when all same, empty when different
- **Validates: Requirements 3.1, 3.2, 4.1, 4.2, 4.3, 4.4, 4.6, 4.7**
- File: `backend/__tests__/jira-consolidation-aggregation.property.test.js`
- [x] 4. Frontend multi-select mode on Ivanti Queue page
- [x] 4.1 Add selection mode toggle and checkbox UI to `frontend/src/components/pages/IvantiQueuePage.js`
- Add "Select" toggle button to page toolbar
- Show checkboxes on each queue item row when selection mode active
- Add "Select All" checkbox in table header
- Display selection count indicator
- Clear selections when selection mode deactivated
- Preserve selections on scroll/re-render within same session
- _Requirements: 1.1, 1.2, 1.3, 1.4, 1.5, 1.6_
- [x] 4.2 Add floating action bar with "Create Jira Ticket" button
- Render floating bar when selection mode active and at least 1 item selected
- Disable "Create Jira Ticket" button when no items selected
- Route to existing single-item modal when exactly 1 item selected
- Route to new Consolidation Modal when 2+ items selected
- _Requirements: 2.1, 2.2, 2.3, 2.4_
- [x] 5. Frontend Consolidation Modal
- [x] 5.1 Create `frontend/src/components/ConsolidationModal.js`
- Accept selected queue items as props
- Call aggregation functions to pre-populate summary, description, cve_id, vendor
- Lock source_context to `ivanti_queue` (read-only display)
- Display item count in modal header/subtitle
- Render scrollable preview list of selected items (finding_title + hostname)
- Allow removing individual items from selection (minimum 2 required)
- Disable submit and show message when fewer than 2 items remain
- Editable summary field with required validation (max 255 chars)
- Editable description textarea
- Editable CVE ID and Vendor fields (optional)
- On submit: POST to create-in-jira, then POST to junction endpoint
- On success: close modal, show success toast, trigger queue page refresh
- On error: display error message, preserve form values
- _Requirements: 3.1, 3.2, 3.3, 3.4, 4.1, 4.5, 5.1, 5.2, 5.4, 5.5, 8.1, 8.2, 8.3, 8.4, 8.5_
- [x] 6. Frontend ticket link badges on queue items
- [x] 6.1 Fetch and display ticket link badges on Ivanti Queue page
- On page load, fetch `GET /api/ivanti/todo-queue/ticket-links`
- For each queue item with an association, render a ticket key badge (e.g., "VULN-789")
- Clicking badge opens Jira URL in new tab
- Refresh links after successful consolidated ticket creation
- _Requirements: 6.3, 6.4, 6.5_
- [ ] 7. Backend property test for junction insert invariant
- [ ]* 7.1 Write property test for junction row count (Property 7)
- **Property 7: Junction table row count equals selected item count** — for N items, exactly N rows inserted
- **Validates: Requirements 5.3, 6.2**
- File: `backend/__tests__/jira-consolidation-junction.property.test.js`
- [x] 8. Final checkpoint
- Ensure all tests pass, ask the user if questions arise.
## Notes
- Tasks marked with `*` are optional property-based tests that can be skipped for faster MVP
- The existing `POST /api/jira-tickets/create-in-jira` endpoint is reused without modification
- The aggregation functions are pure and extracted into a utility module for easy testing
- The junction table insert happens as a second request after ticket creation — if it fails, the ticket still exists in Jira (partial success scenario handled with warning)
- The Consolidation Modal is a separate component from the existing Creation Modal to avoid overcomplicating the single-item flow
## Task Dependency Graph
```json
{
"waves": [
{ "id": 0, "tasks": ["1.1"] },
{ "id": 1, "tasks": ["2.1", "2.2", "3.1"] },
{ "id": 2, "tasks": ["3.2", "4.1"] },
{ "id": 3, "tasks": ["4.2", "5.1"] },
{ "id": 4, "tasks": ["6.1", "7.1"] }
]
}
```