# Implementation Plan - [x] 1. Write bug condition exploration test - **Property 1: Bug Condition** — Jira API Compliance Violations - **CRITICAL**: This test MUST FAIL on unfixed code — failure confirms the bugs exist - **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 three compliance violations - **Scoped PBT Approach**: Scope properties to the three concrete bug conditions: 1. `searchIssues()` sends POST instead of GET — generate random JQL strings and assert the HTTP method captured is `GET` and the request path starts with `/rest/api/2/search?` with query parameters (not a JSON body) 2. `getIssue()` sends a single-issue GET to `/rest/api/2/issue/{key}` — generate random issue keys and assert the request path contains `/rest/api/2/search` (not `/rest/api/2/issue/`) 3. `searchIssuesByKeys()` builds JQL without `project =` — generate random arrays of issue keys and assert the JQL string passed to the search contains `project =` - Mock `jiraRequest` to capture HTTP method, URL path, and body arguments without making real HTTP calls - Use `fast-check` arbitraries to generate JQL strings, issue keys (e.g., `fc.tuple(fc.stringMatching(/^[A-Z]{2,6}$/), fc.integer({ min: 1, max: 99999 }))` for `KEY-123` patterns), and key arrays - Test file: `backend/__tests__/jira-api-compliance.property.test.js` - Run test on UNFIXED code - **EXPECTED OUTCOME**: Test FAILS (this is correct — it proves the bugs exist) - Document counterexamples found: `searchIssues()` uses POST, `getIssue()` hits `/rest/api/2/issue/{key}`, `searchIssuesByKeys()` JQL lacks `project =` - Mark task complete when test is written, run, and failure is documented - _Requirements: 1.1, 1.2, 1.3_ - [x] 2. Write preservation property tests (BEFORE implementing fix) - **Property 2: Preservation** — Unchanged Jira API Functions - **IMPORTANT**: Follow observation-first methodology - Observe behavior on UNFIXED code for non-buggy functions: - `createIssue({ project: { key: 'TEST' }, summary: 'x', issuetype: { name: 'Task' } })` sends `POST` to `/rest/api/2/issue` with JSON body containing `{ fields: {...} }` - `updateIssue('TEST-1', { summary: 'y' })` sends `PUT` to `/rest/api/2/issue/TEST-1` with JSON body containing `{ fields: {...} }` - `addComment('TEST-1', 'comment text')` sends `POST` to `/rest/api/2/issue/TEST-1/comment` with JSON body containing `{ body: 'comment text' }` - `transitionIssue('TEST-1', '5')` sends `POST` to `/rest/api/2/issue/TEST-1/transitions` with JSON body containing `{ transition: { id: '5' } }` - `getTransitions('TEST-1')` sends `GET` to `/rest/api/2/issue/TEST-1/transitions` - `testConnection()` sends `GET` to `/rest/api/2/myself` - Write property-based tests using `fast-check` that verify for all generated inputs: 1. `createIssue()` always sends `POST /rest/api/2/issue` with `{ fields }` body — generate random field objects 2. `updateIssue()` always sends `PUT /rest/api/2/issue/{key}` with `{ fields }` body — generate random keys and field objects 3. `addComment()` always sends `POST /rest/api/2/issue/{key}/comment` with `{ body }` — generate random keys and comment strings 4. `transitionIssue()` always sends `POST /rest/api/2/issue/{key}/transitions` with `{ transition: { id } }` — generate random keys and transition IDs 5. `getTransitions()` always sends `GET /rest/api/2/issue/{key}/transitions` — generate random keys 6. `testConnection()` always sends `GET /rest/api/2/myself` 7. Response shape: `searchIssues()` returns `{ ok, data: { total, issues } }` and `getIssue()` returns `{ ok, data: }` — verify shape is preserved - Mock `jiraRequest` to capture method, path, body and return appropriate mock responses - Test file: `backend/__tests__/jira-api-preservation.property.test.js` - Verify tests pass on UNFIXED code - **EXPECTED OUTCOME**: Tests PASS (this 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, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10_ - [x] 3. Fix the core API helper (`backend/helpers/jiraApi.js`) - [x] 3.1 Convert `searchIssues()` from POST to GET with query parameters - Replace `jiraPost('/rest/api/2/search', body)` with `jiraGet('/rest/api/2/search?jql=...&fields=...&maxResults=...&startAt=...')` - URL-encode JQL string with `encodeURIComponent(jql)` - Comma-join and encode fields array with `encodeURIComponent(fields.join(','))` - Encode `maxResults` and `startAt` as query parameters - Remove the JSON body object `{ jql, startAt, maxResults, fields }` - Preserve the `res.status === 200` check and `JSON.parse(res.body)` response parsing - Preserve the `{ ok, data }` return shape - _Bug_Condition: searchIssues uses POST /rest/api/2/search with JSON body_ - _Expected_Behavior: searchIssues uses GET /rest/api/2/search?jql=&fields=&maxResults=&startAt=_ - _Preservation: Response shape { ok, data: { total, issues } } unchanged_ - _Requirements: 2.1_ - [x] 3.2 Add project scoping to `searchIssuesByKeys()` JQL - Change JQL from `` key in (${keyList}) AND updated >= -24h `` to `` key in (${keyList}) AND updated >= -24h AND project = ${JIRA_PROJECT_KEY} `` - `JIRA_PROJECT_KEY` is already available in module scope - _Bug_Condition: searchIssuesByKeys JQL lacks project = clause_ - _Expected_Behavior: JQL includes project = JIRA_PROJECT_KEY_ - _Preservation: Return shape and searchIssues delegation unchanged_ - _Requirements: 2.2_ - [x] 3.3 Refactor `getIssue()` to delegate to `searchIssues()` via JQL - Replace `jiraGet('/rest/api/2/issue/${encodeURIComponent(issueKey)}?fields=...')` with a call to `searchIssues()` using JQL `key = "${issueKey}" AND project = ${JIRA_PROJECT_KEY}` and `maxResults: 1` - Extract `data.issues[0]` from search results to return as `{ ok: true, data: }` - Return `{ ok: false, status: 404, body: 'Issue not found' }` when search returns empty results - Preserve the `{ ok, data: }` return shape for callers - _Bug_Condition: getIssue sends GET /rest/api/2/issue/{key} (single-issue GET)_ - _Expected_Behavior: getIssue delegates to searchIssues with JQL key = "{key}" AND project = KEY_ - _Preservation: Return shape { ok, data: { key, fields } } unchanged_ - _Requirements: 2.3_ - [x] 3.4 Verify bug condition exploration test now passes - **Property 1: Expected Behavior** — Jira API Compliance Violations - **IMPORTANT**: Re-run the SAME test from task 1 — do NOT write a new test - The test from task 1 encodes the expected behavior - When this test passes, it confirms the expected behavior is satisfied - Run `npx jest backend/__tests__/jira-api-compliance.property.test.js --no-cache` - **EXPECTED OUTCOME**: Test PASSES (confirms bugs are fixed) - _Requirements: 2.1, 2.2, 2.3_ - [x] 3.5 Verify preservation tests still pass - **Property 2: Preservation** — Unchanged Jira API Functions - **IMPORTANT**: Re-run the SAME tests from task 2 — do NOT write new tests - Run `npx jest backend/__tests__/jira-api-preservation.property.test.js --no-cache` - **EXPECTED OUTCOME**: Tests PASS (confirms no regressions) - Confirm all unchanged functions still produce the same HTTP method, path, and body - [x] 4. Update the UAT test script (`backend/scripts/jira-uat-test.js`) - [x] 4.1 Update test case 3 name to reflect JQL-based pattern - Change `'3. Get Single Issue (GET /issue/{key})'` to `'3. Get Single Issue (JQL search)'` - The test body calls `jiraApi.getIssue()` which now delegates to JQL search — no logic change needed in the test function itself - _Requirements: 2.4_ - [x] 4.2 Update test case 8 name to reflect GET method - Change `'8. JQL Search (POST /search)'` to `'8. JQL Search (GET /search)'` - Add project-scoped JQL to the test: include `AND project = ${jiraApi.JIRA_PROJECT_KEY}` in the JQL string passed to `searchIssues()` - _Requirements: 2.5_ - [x] 4.3 Update test case 9 to verify project scoping - Add a log entry or assertion that the bulk key search includes project scoping - The underlying `searchIssuesByKeys()` now includes `project = ` — the test validates the function works correctly with the compliant JQL - _Requirements: 2.5_ - [x] 5. Update the API documentation (`docs/jira-api-use-cases.md`) - [x] 5.1 Update compliance summary table - Change "Bulk reads via JQL" row endpoint from `POST /rest/api/2/search` to `GET /rest/api/2/search` - Add a row for "Single-issue fetch" describing JQL-based lookup via `GET /rest/api/2/search?jql=key="KEY"&...` - _Requirements: 2.6, 2.7_ - [x] 5.2 Update Use Case 3 (Get Single Issue) - Change endpoint from `GET /rest/api/2/issue/{issueKey}?fields=...` to `GET /rest/api/2/search?jql=key="ISSUE-KEY" AND project=&fields=...&maxResults=1` - Update the description to explain the JQL-based pattern - _Requirements: 2.7_ - [x] 5.3 Update Use Case 8 (JQL Search / Bulk Sync) - Change endpoint from `POST /rest/api/2/search` to `GET /rest/api/2/search?jql=...&fields=...&maxResults=...&startAt=...` - Update JQL pattern to include `project = ` scoping - _Requirements: 2.6_ - [x] 5.4 Update Use Case 9 (Issue Lookup) - Change endpoint from `GET /rest/api/2/issue/{issueKey}?fields=...` to `GET /rest/api/2/search?jql=key="ISSUE-KEY" AND project=&fields=...&maxResults=1` - Update the description to match the JQL-based lookup pattern - _Requirements: 2.7_ - [x] 6. Checkpoint — Ensure all tests pass - Run `npx jest backend/__tests__/jira-api-compliance.property.test.js --no-cache` — all bug condition tests pass - Run `npx jest backend/__tests__/jira-api-preservation.property.test.js --no-cache` — all preservation tests pass - Run `npx jest --no-cache` — all existing tests in the project still pass - Ensure all tests pass, ask the user if questions arise.