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:
@@ -0,0 +1 @@
|
||||
{"specId": "3e04c019-5516-446d-867c-bdb52b4f516a", "workflowType": "requirements-first", "specType": "bugfix"}
|
||||
29
.kiro/specs/ivanti-queue-clear-completed-fix/bugfix.md
Normal file
29
.kiro/specs/ivanti-queue-clear-completed-fix/bugfix.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# Bugfix Requirements Document
|
||||
|
||||
## Introduction
|
||||
|
||||
The "Clear Completed" button in the Ivanti Queue panel fails silently when clicked. Completed queue items that have associated rows in the `jira_ticket_queue_items` junction table cannot be deleted because the foreign key constraint (`queue_item_id REFERENCES ivanti_todo_queue(id)`) lacks `ON DELETE CASCADE`. PostgreSQL rejects the deletion with a FK violation, the backend returns a 500, and the frontend discards the error — leaving the user with no feedback and no action taken.
|
||||
|
||||
Queue items are marked complete once a Jira ticket has been opened from them. The Jira ticket continues to live independently in the `jira_tickets` table — the junction table link is purely historical at that point. Clearing completed items should remove both the queue item and its junction table references without affecting the Jira ticket itself.
|
||||
|
||||
## Bug Analysis
|
||||
|
||||
### Current Behavior (Defect)
|
||||
|
||||
1.1 WHEN a user clicks "Clear Completed" and one or more completed queue items have associated rows in `jira_ticket_queue_items` THEN the system returns a 500 error due to a foreign key violation and no items are deleted
|
||||
|
||||
1.2 WHEN the backend DELETE query fails with a FK constraint error THEN the system returns a generic 500 response and the frontend silently ignores the failure, providing no user feedback
|
||||
|
||||
### Expected Behavior (Correct)
|
||||
|
||||
2.1 WHEN a user clicks "Clear Completed" and one or more completed queue items have associated rows in `jira_ticket_queue_items` THEN the system SHALL first delete the associated `jira_ticket_queue_items` rows and then delete the completed queue items successfully within a transaction
|
||||
|
||||
2.2 WHEN the "Clear Completed" operation succeeds THEN the system SHALL return a success response and the frontend SHALL remove all completed items from the displayed list
|
||||
|
||||
### Unchanged Behavior (Regression Prevention)
|
||||
|
||||
3.1 WHEN a user clicks "Clear Completed" and no completed queue items have associated rows in `jira_ticket_queue_items` THEN the system SHALL CONTINUE TO delete those items directly and return a success response
|
||||
|
||||
3.2 WHEN a user clicks "Clear Completed" and there are no completed queue items THEN the system SHALL CONTINUE TO return a success response with zero deleted count
|
||||
|
||||
3.3 WHEN queue items are in a non-complete status (pending, in_progress) THEN the system SHALL CONTINUE TO leave those items untouched regardless of whether they have associated `jira_ticket_queue_items` rows
|
||||
199
.kiro/specs/ivanti-queue-clear-completed-fix/design.md
Normal file
199
.kiro/specs/ivanti-queue-clear-completed-fix/design.md
Normal file
@@ -0,0 +1,199 @@
|
||||
# Ivanti Queue Clear Completed Bugfix Design
|
||||
|
||||
## Overview
|
||||
|
||||
The "Clear Completed" endpoint (`DELETE /api/ivanti/todo-queue/completed`) fails with a 500 error when completed queue items have associated rows in the `jira_ticket_queue_items` junction table. The foreign key constraint on `queue_item_id REFERENCES ivanti_todo_queue(id)` lacks `ON DELETE CASCADE`, so PostgreSQL rejects the deletion. The fix wraps the operation in a transaction that deletes junction table references before deleting the queue items themselves.
|
||||
|
||||
## Glossary
|
||||
|
||||
- **Bug_Condition (C)**: The condition that triggers the bug — when one or more completed queue items have associated rows in `jira_ticket_queue_items`
|
||||
- **Property (P)**: The desired behavior — all completed items (with or without junction table links) are deleted atomically, along with their junction table references
|
||||
- **Preservation**: Existing behavior for items without junction table links, empty result sets, and non-complete items must remain unchanged
|
||||
- **`pool`**: The PostgreSQL connection pool exported from `backend/db.js`
|
||||
- **`jira_ticket_queue_items`**: Junction table linking `jira_tickets` to `ivanti_todo_queue` items (created by `add_multi_item_jira_ticket.js` migration)
|
||||
- **`ivanti_todo_queue`**: Main queue table storing user work items with `status` column (`pending`, `complete`)
|
||||
|
||||
## Bug Details
|
||||
|
||||
### Bug Condition
|
||||
|
||||
The bug manifests when a user clicks "Clear Completed" and at least one of their completed queue items has a row in `jira_ticket_queue_items` referencing it. The current handler issues a bare `DELETE FROM ivanti_todo_queue WHERE user_id = $1 AND status = 'complete'` which PostgreSQL rejects because child rows exist in the junction table.
|
||||
|
||||
**Formal Specification:**
|
||||
```
|
||||
FUNCTION isBugCondition(input)
|
||||
INPUT: input of type ClearCompletedRequest
|
||||
OUTPUT: boolean
|
||||
|
||||
completedItems := SELECT id FROM ivanti_todo_queue
|
||||
WHERE user_id = input.userId AND status = 'complete'
|
||||
|
||||
linkedItems := SELECT queue_item_id FROM jira_ticket_queue_items
|
||||
WHERE queue_item_id IN completedItems
|
||||
|
||||
RETURN linkedItems.length > 0
|
||||
END FUNCTION
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
- User has 3 completed items, 2 have junction table links → DELETE fails with FK violation, 0 items deleted (bug)
|
||||
- User has 1 completed item with a junction table link → DELETE fails, item remains (bug)
|
||||
- User has 5 completed items, all have junction table links → DELETE fails, all remain (bug)
|
||||
- User has 3 completed items, none have junction table links → DELETE succeeds (no bug, preservation case)
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
### Preservation Requirements
|
||||
|
||||
**Unchanged Behaviors:**
|
||||
- Completed items without junction table links are deleted directly and a success response is returned
|
||||
- When no completed items exist, the endpoint returns `{ message: 'Completed items cleared.', deleted: 0 }`
|
||||
- Pending/in-progress items are never touched regardless of junction table links
|
||||
- The `jira_tickets` table is never modified (tickets persist independently of queue items)
|
||||
- Auth middleware (`requireAuth`, `requireGroup`) continues to enforce access control
|
||||
- Response shape remains `{ message: string, deleted: number }`
|
||||
|
||||
**Scope:**
|
||||
All inputs where `isBugCondition` returns false should be completely unaffected by this fix. This includes:
|
||||
- Users with no completed items
|
||||
- Users with completed items that have no junction table references
|
||||
- Any request targeting non-complete statuses
|
||||
- Mouse/UI interactions unrelated to the "Clear Completed" button
|
||||
|
||||
## Hypothesized Root Cause
|
||||
|
||||
Based on the bug description and code inspection, the root cause is confirmed:
|
||||
|
||||
1. **Missing cascade handling in application code**: The `DELETE FROM ivanti_todo_queue WHERE user_id = $1 AND status = 'complete'` query does not account for the FK constraint added by the `add_multi_item_jira_ticket.js` migration. The junction table was added after the original endpoint was written.
|
||||
|
||||
2. **FK constraint without ON DELETE CASCADE**: The migration creates `queue_item_id INTEGER NOT NULL REFERENCES ivanti_todo_queue(id)` without specifying `ON DELETE CASCADE`. This is a deliberate design choice (junction table links should be explicitly managed), but the delete endpoint was never updated to handle it.
|
||||
|
||||
3. **No transaction wrapping**: The current handler uses a single query without a transaction. Even if it attempted to delete junction rows first, without a transaction there would be a race condition window.
|
||||
|
||||
## Correctness Properties
|
||||
|
||||
Property 1: Bug Condition - Clear Completed With Junction Table Links
|
||||
|
||||
_For any_ set of completed queue items belonging to a user where at least one item has associated rows in `jira_ticket_queue_items`, the fixed clear completed operation SHALL delete all associated `jira_ticket_queue_items` rows first, then delete all completed queue items for that user, atomically within a transaction, and return a success response with the correct deleted count.
|
||||
|
||||
**Validates: Requirements 2.1, 2.2**
|
||||
|
||||
Property 2: Preservation - Clear Completed Without Junction Table Links
|
||||
|
||||
_For any_ set of completed queue items belonging to a user where NO items have associated rows in `jira_ticket_queue_items` (or where no completed items exist at all), the fixed clear completed operation SHALL produce the same result as the original function — deleting all completed items and returning a success response with the correct deleted count.
|
||||
|
||||
**Validates: Requirements 3.1, 3.2, 3.3**
|
||||
|
||||
## Fix Implementation
|
||||
|
||||
### Changes Required
|
||||
|
||||
**File**: `backend/routes/ivantiTodoQueue.js`
|
||||
|
||||
**Function**: `router.delete('/completed', ...)` handler
|
||||
|
||||
**Specific Changes**:
|
||||
|
||||
1. **Acquire a dedicated client from the pool**: Replace `pool.query(...)` with `pool.connect()` to get a client that supports transactions.
|
||||
|
||||
2. **Begin a transaction**: Issue `BEGIN` before any data-modifying queries.
|
||||
|
||||
3. **Select completed item IDs**: Query `SELECT id FROM ivanti_todo_queue WHERE user_id = $1 AND status = 'complete'` to get the set of IDs to delete.
|
||||
|
||||
4. **Early exit for empty set**: If no completed items exist, commit and return `{ deleted: 0 }` immediately.
|
||||
|
||||
5. **Delete junction table references**: Issue `DELETE FROM jira_ticket_queue_items WHERE queue_item_id = ANY($1::int[])` with the collected IDs.
|
||||
|
||||
6. **Delete queue items by ID**: Issue `DELETE FROM ivanti_todo_queue WHERE id = ANY($1::int[])` using the same ID set (more precise than re-filtering by status).
|
||||
|
||||
7. **Commit transaction**: Issue `COMMIT` on success.
|
||||
|
||||
8. **Rollback on error**: Wrap in try/catch, issue `ROLLBACK` on any failure, then return 500.
|
||||
|
||||
9. **Release client**: Always release the client back to the pool in a `finally` block.
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
### Validation Approach
|
||||
|
||||
The testing strategy follows a two-phase approach: first, surface counterexamples that demonstrate the bug on unfixed code, then verify the fix works correctly and preserves existing behavior.
|
||||
|
||||
### Exploratory Bug Condition Checking
|
||||
|
||||
**Goal**: Surface counterexamples that demonstrate the bug BEFORE implementing the fix. Confirm the FK violation root cause.
|
||||
|
||||
**Test Plan**: Mock the `pool.connect()` / `pool.query()` pattern to simulate the FK constraint violation. Write tests that attempt to clear completed items when junction table references exist and assert the operation fails on unfixed code.
|
||||
|
||||
**Test Cases**:
|
||||
1. **Single linked item**: One completed item with a junction table reference — DELETE fails (will fail on unfixed code)
|
||||
2. **Mixed linked/unlinked items**: Some completed items have links, some don't — DELETE fails for all (will fail on unfixed code)
|
||||
3. **All items linked**: Every completed item has junction table references — DELETE fails (will fail on unfixed code)
|
||||
4. **Multiple links per item**: One completed item with multiple junction table rows — DELETE fails (will fail on unfixed code)
|
||||
|
||||
**Expected Counterexamples**:
|
||||
- The simple DELETE query throws a FK violation error
|
||||
- The catch block returns 500 and no items are deleted
|
||||
- Possible cause confirmed: missing junction table cleanup before parent row deletion
|
||||
|
||||
### Fix Checking
|
||||
|
||||
**Goal**: Verify that for all inputs where the bug condition holds, the fixed function produces the expected behavior.
|
||||
|
||||
**Pseudocode:**
|
||||
```
|
||||
FOR ALL input WHERE isBugCondition(input) DO
|
||||
result := clearCompleted_fixed(input)
|
||||
ASSERT result.status = 200
|
||||
ASSERT result.body.deleted = count(completedItems)
|
||||
ASSERT jira_ticket_queue_items has no rows for deleted IDs
|
||||
ASSERT ivanti_todo_queue has no completed rows for user
|
||||
ASSERT pending items unchanged
|
||||
ASSERT jira_tickets table unchanged
|
||||
END FOR
|
||||
```
|
||||
|
||||
### Preservation Checking
|
||||
|
||||
**Goal**: Verify that for all inputs where the bug condition does NOT hold, the fixed function produces the same result as the original function.
|
||||
|
||||
**Pseudocode:**
|
||||
```
|
||||
FOR ALL input WHERE NOT isBugCondition(input) DO
|
||||
ASSERT clearCompleted_original(input) = clearCompleted_fixed(input)
|
||||
END FOR
|
||||
```
|
||||
|
||||
**Testing Approach**: Property-based testing is recommended for preservation checking because:
|
||||
- It generates many test cases automatically across the input domain
|
||||
- It catches edge cases (empty sets, single items, large batches)
|
||||
- It provides strong guarantees that behavior is unchanged for all non-buggy inputs
|
||||
|
||||
**Test Plan**: Observe behavior on UNFIXED code for cases without junction table links (these succeed today), then write property-based tests capturing that behavior.
|
||||
|
||||
**Test Cases**:
|
||||
1. **No completed items**: Verify returns `{ deleted: 0 }` — same as before
|
||||
2. **Completed items without links**: Verify all are deleted and count is correct — same as before
|
||||
3. **Pending items untouched**: Verify non-complete items are never affected — same as before
|
||||
4. **Response shape preserved**: Verify `{ message, deleted }` structure unchanged
|
||||
|
||||
### Unit Tests
|
||||
|
||||
- Mock `pool.connect()` and verify correct query sequence within transaction (BEGIN → SELECT → DELETE junction → DELETE queue → COMMIT)
|
||||
- Verify ROLLBACK is called on any query failure
|
||||
- Verify client is always released in finally block
|
||||
- Test edge case: empty completed set triggers early COMMIT and returns `{ deleted: 0 }`
|
||||
|
||||
### Property-Based Tests
|
||||
|
||||
- Generate random sets of queue items (mix of pending/complete, with/without junction links) and verify the transaction deletes exactly the right rows
|
||||
- Generate random configurations without junction links and verify identical behavior to original code
|
||||
- Generate random user IDs and verify isolation (one user's clear doesn't affect another's items)
|
||||
|
||||
### Integration Tests
|
||||
|
||||
- End-to-end test with actual FK constraints: insert queue items + junction rows, call endpoint, verify both tables cleaned up
|
||||
- Verify atomicity: if the junction DELETE succeeds but queue DELETE fails, nothing is committed
|
||||
- Verify the endpoint still works for the simple case (no junction rows)
|
||||
|
||||
**Test file**: `backend/__tests__/ivanti-queue-clear-completed-fix.test.js`
|
||||
86
.kiro/specs/ivanti-queue-clear-completed-fix/tasks.md
Normal file
86
.kiro/specs/ivanti-queue-clear-completed-fix/tasks.md
Normal file
@@ -0,0 +1,86 @@
|
||||
# Implementation Plan
|
||||
|
||||
- [x] 1. Write bug condition exploration test
|
||||
- **Property 1: Bug Condition** - FK Violation on Clear Completed With Junction Table Links
|
||||
- **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 FK violation bug exists
|
||||
- **Scoped PBT Approach**: Scope the property to the concrete failing case — completed queue items that have associated `jira_ticket_queue_items` rows
|
||||
- Bug condition from design: `isBugCondition(input)` returns true when `linkedItems.length > 0` (completed items have junction table references)
|
||||
- Test file: `backend/__tests__/ivanti-queue-clear-completed-fix.property.test.js`
|
||||
- Mock `pool.query` to simulate FK constraint violation when DELETE is issued against `ivanti_todo_queue` while child rows exist in `jira_ticket_queue_items`
|
||||
- Assert that the current (unfixed) handler returns 500 and deletes zero items
|
||||
- Run test on UNFIXED code
|
||||
- **EXPECTED OUTCOME**: Test FAILS (confirms the bug — the handler crashes with FK violation instead of succeeding)
|
||||
- Document counterexamples: "DELETE FROM ivanti_todo_queue fails with FK violation when junction rows reference completed items"
|
||||
- Mark task complete when test is written, run, and failure is documented
|
||||
- _Requirements: 1.1_
|
||||
|
||||
- [x] 2. Write preservation property tests (BEFORE implementing fix)
|
||||
- **Property 2: Preservation** - Clear Completed Without Junction Table Links
|
||||
- **IMPORTANT**: Follow observation-first methodology
|
||||
- **GOAL**: Verify that the unfixed code already handles the non-bug-condition cases correctly, establishing a baseline to preserve
|
||||
- Test file: `backend/__tests__/ivanti-queue-clear-completed-fix.property.test.js`
|
||||
- Observe: When no completed items have junction table links, the simple DELETE succeeds and returns correct count
|
||||
- Observe: When no completed items exist, the endpoint returns `{ message: 'Completed items cleared.', deleted: 0 }`
|
||||
- Observe: Pending/in-progress items are never touched by the DELETE
|
||||
- Write property-based tests generating random sets of completed items WITHOUT junction table links and verify:
|
||||
- All completed items for the user are deleted
|
||||
- Response is `{ message: 'Completed items cleared.', deleted: N }` where N matches count
|
||||
- Non-complete items remain untouched
|
||||
- Other users' items remain untouched
|
||||
- Run tests 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.1, 3.2, 3.3_
|
||||
|
||||
- [x] 3. Fix for FK violation on clear completed queue items
|
||||
|
||||
- [x] 3.1 Implement the fix
|
||||
- File: `backend/routes/ivantiTodoQueue.js`
|
||||
- Replace the simple `pool.query(DELETE...)` in the `router.delete('/completed', ...)` handler with a transaction-based approach:
|
||||
- 1. Acquire a dedicated client via `pool.connect()`
|
||||
- 2. Issue `BEGIN`
|
||||
- 3. Select completed item IDs: `SELECT id FROM ivanti_todo_queue WHERE user_id = $1 AND status = 'complete'`
|
||||
- 4. If no IDs found, `COMMIT` and return `{ deleted: 0 }` early
|
||||
- 5. Delete junction table references: `DELETE FROM jira_ticket_queue_items WHERE queue_item_id = ANY($1::int[])`
|
||||
- 6. Delete queue items: `DELETE FROM ivanti_todo_queue WHERE id = ANY($1::int[])`
|
||||
- 7. `COMMIT` on success
|
||||
- 8. `ROLLBACK` on any error, then return 500
|
||||
- 9. Always release client in `finally` block
|
||||
- _Bug_Condition: isBugCondition(input) where completedItems have rows in jira_ticket_queue_items_
|
||||
- _Expected_Behavior: All completed items and their junction references deleted atomically, returns success with correct count_
|
||||
- _Preservation: Items without junction links still deleted; empty sets return deleted: 0; pending items untouched_
|
||||
- _Requirements: 2.1, 2.2, 3.1, 3.2, 3.3_
|
||||
|
||||
- [x] 3.2 Verify bug condition exploration test now passes
|
||||
- **Property 1: Expected Behavior** - FK Violation on Clear Completed With Junction Table Links
|
||||
- **IMPORTANT**: Re-run the SAME test from task 1 — do NOT write a new test
|
||||
- The test from task 1 encodes the expected behavior (successful deletion with junction cleanup)
|
||||
- When this test passes, it confirms the expected behavior is satisfied
|
||||
- Run: `npx jest backend/__tests__/ivanti-queue-clear-completed-fix.property.test.js --run`
|
||||
- **EXPECTED OUTCOME**: Test PASSES (confirms bug is fixed — transaction deletes junction rows then queue items)
|
||||
- _Requirements: 2.1, 2.2_
|
||||
|
||||
- [x] 3.3 Verify preservation tests still pass
|
||||
- **Property 2: Preservation** - Clear Completed Without Junction Table Links
|
||||
- **IMPORTANT**: Re-run the SAME tests from task 2 — do NOT write new tests
|
||||
- Run preservation property tests from step 2
|
||||
- Run: `npx jest backend/__tests__/ivanti-queue-clear-completed-fix.property.test.js --run`
|
||||
- **EXPECTED OUTCOME**: Tests PASS (confirms no regressions — non-linked items still deleted correctly)
|
||||
- Confirm all tests still pass after fix (no regressions)
|
||||
|
||||
- [x] 4. Write unit tests for transaction logic
|
||||
- Test file: `backend/__tests__/ivanti-queue-clear-completed-fix.test.js`
|
||||
- Mock `pool.connect()` and verify correct query sequence: BEGIN → SELECT IDs → DELETE junction → DELETE queue → COMMIT
|
||||
- Verify ROLLBACK is called when any query in the transaction fails
|
||||
- Verify client is always released in the `finally` block (even on error)
|
||||
- Test edge case: empty completed set triggers early COMMIT and returns `{ deleted: 0 }`
|
||||
- Test that response shape remains `{ message: 'Completed items cleared.', deleted: N }`
|
||||
- _Requirements: 2.1, 2.2, 3.1, 3.2_
|
||||
|
||||
- [x] 5. Checkpoint — Ensure all tests pass
|
||||
- Run full test suite: `npx jest backend/__tests__/ivanti-queue-clear-completed-fix --run`
|
||||
- Ensure all property tests and unit tests pass
|
||||
- Ask the user if questions arise
|
||||
Reference in New Issue
Block a user