# Jira → PR Fix Pipeline Pipeline that detects Jira tickets assigned to the bot, triages them, applies a fix using a Cursor agent, validates the build, and opens a PR on GitHub. --- ## Overview ``` Jira ("To Do" tickets assigned to the bot) │ ▼ JiraPoller ─── every 5s ───► searches tickets by JQL │ enqueues all results ▼ JobQueue (concurrency 1, max 2 retries, timeout 35min) │ ▼ FixPipeline ├── 1. Move ticket to "In Progress" ├── 2. Fetch issue (description, attachments, comments) ├── 3. Sync repos (clone/pull + branch fix/KEY-summary) ├── 4. Sync remote rules repos ├── 5. Save images to .hu-bot/ │ ├── PHASE 1: Triage (read-only agent, timeout 16min) │ ├── NEEDS_INFO → comment on Jira, return to "To Do", abort │ └── PROCEED → continue │ ├── PHASE 2: Fix (write-enabled agent, timeout 30min) │ └── no changes → comment on Jira, abort │ └── For each repo with changes: ├── getDiffStats (safety checks) ├── [multi-repo] create Jira subtask + dedicated branch ├── commitAndPush ├── validateBuild (install + check scripts) │ └── if fails → retry with followup agent (15min) ├── createPr on GitHub └── addLabels based on flow (hotfix / bugfix / default) ``` --- ## 1. Poller (`JiraPoller`) **File:** `src/poller/jira-poller.ts` **Interval:** 5 seconds (`POLLER.INTERVAL_MS`) ### What it does each cycle 1. **Guard conditions** — skips if: - A poll is already in progress - The queue is busy - A `pr_comment` job is running (both job types respect each other) 2. **Resolves the bot's `accountId`** on first run (looks up by email via Jira API). 3. Runs the JQL: ``` assignee = "" AND status = "To Do" ORDER BY created DESC ``` 4. Enqueues **all results** as `jira` jobs. ### Enqueued payload ```typescript { jobKind: "jira", issueId: string, // Jira internal ID issueKey: string, // e.g. "SQDP-3480" summary: string, description: string, } ``` --- ## 2. Queue **Concurrency:** 1. **Max retries:** 2 (`QUEUE.MAX_RETRIES`). **Job timeout:** 35 minutes (`QUEUE.JOB_TIMEOUT_MS`). --- ## 3. Pipeline (`FixPipeline`) **File:** `src/pipeline/fix-pipeline.ts` ### 3.1 Move ticket to "In Progress" Transitions the Jira issue to `"In Progress"` before starting. If it fails, execution continues (non-blocking warning). ### 3.2 Fetch the full issue Fetches from Jira: - Summary, description, status, flow (`regular` / `hotfix` / `bugfix`) - Parent ticket (if subtask): key, summary, description, type - Acceptance criteria, stacktrace - Previous comments (for agent context) - Attachments (images to download) ### 3.3 Repo sync (`syncAllRepos`) For **all** repos in the registry (`repos.json`): ``` checkoutBase(develop) → cleans the working tree and pulls latest createBranch("fix/KEY-summary-slug") installDependencies() → yarn/npm install (non-blocking) ``` The branch name is generated with `buildBranchName(issueKey, summary)`: `fix/SQDP-3480-login-button-not-working-on-mobile` > If no repo can be synced, the pipeline aborts with `success: false`. ### 3.4 Remote rules sync (`syncRemoteRulesRepos`) If any repo in `repos.json` has `remoteRulesRepo` set, that rules repo is cloned (or pulled) and its `.mdc` files are **symlinked** into `.cursor/rules/` of the code repo. This injects project-specific rules into the agent at runtime. ### 3.5 Save images to `.hu-bot/` Downloads image attachments from the ticket (and its parent) to `workdir/.hu-bot/`. - Max 5 images (`ATTACHMENTS.MAX_IMAGES`) - Max 5MB per image (`ATTACHMENTS.MAX_SIZE_BYTES`) The path is mentioned in the prompt so the agent can open them using its tools. Images are deleted at the end of the pipeline (`cleanupImages`). ### 3.6 Load repo contexts (`loadAllRepoContexts`) For each repo, reads agent rules from: 1. `/.cursor/rules/hu-agent.mdc` (rules inside the repo; default index filename) 2. `repos.json[repo].rulesFile` (local bot-side path, fallback) The `.mdc` file can be an index with relative links to other `.md` files. They are automatically concatenated up to a limit of 8000 chars. --- ## 4. Phase 1: Triage **Timeout:** 16 minutes (`CURSOR_AGENT.TRIAGE_TIMEOUT_MS`) The agent receives the triage prompt (`buildTriagePrompt`) and must respond with exactly one of these formats: ``` VERDICT: PROCEED SUMMARY: ``` ``` VERDICT: NEEDS_INFO SUMMARY: QUESTIONS: - - ``` ### If NEEDS_INFO 1. Posts a comment on Jira with the analysis and questions. 2. Transitions the ticket back to `"To Do"`. 3. Returns `outcome: "needs_info"` — the ticket waits for more information. ### If PROCEED Continues to the fix phase. > **Fallback:** If the triage agent fails (exit code ≠ 0), `PROCEED` is assumed to avoid blocking valid tickets. --- ## 5. Phase 2: Fix **Timeout:** 30 minutes (`CURSOR_AGENT.INITIAL_TIMEOUT_MS`) The agent receives the fix prompt (`buildFixPrompt`), which includes: - General bot rules (`rules/general.mdc`) - The triage summary as prior context - The ticket data (bug report, description, comments, images) - Description and rules for **all** available repos The agent works in `workdir/` with access to all cloned repos. ### If the agent produces no changes Posts a comment on Jira with the agent output ("I investigated but could not produce a fix") and aborts. ### If the agent fails (exit code ≠ 0) Posts a comment on Jira with the agent summary and aborts. --- ## 6. Post-fix: per repo with changes ### 6.1 Safety check (`getDiffStats`) Validates diff limits before committing: | Limit | Value | |---|---| | Max files changed | 10 | | Max lines changed | 500 | | Max files deleted | 3 | | Forbidden paths | `.github/workflows/`, `.travis.yml`, etc. | Lockfiles (`yarn.lock`, `package-lock.json`, etc.) are automatically excluded from the commit. ### 6.2 Subtasks for multi-repo (optional) If the agent modified **more than one repo**, a Jira subtask is created per repo: ``` | ``` Each subtask has its own branch: `fix/KEY-SUBTASK-slug`. If subtask creation fails, the original issue is used as fallback. ### 6.3 Commit and push ``` fix(): ``` Push with `--force` to overwrite if the branch already existed. ### 6.4 Build validation (`validateBuild`) 1. **Install** — if `node_modules` doesn't exist or `package.json` changed. 2. **Check scripts** — runs scripts defined in `repos.json[repo].checkScripts` (e.g. `typecheck`, `lint`). If no scripts are defined, attempts `build`. For each failing script: - If exit code is 127 (binary not available), it is skipped. - If errors are **not in files modified by the bot** (pre-existing errors), they are skipped. - If errors **are in bot-modified files**, validation fails. #### Followup agent retry If the build fails, a second agent (`buildFollowupPrompt`) is run with the error output to fix the issues. Timeout: 15 minutes. If the build still fails after the retry, that repo is skipped (`continue`) — no PR is created for it. ### 6.5 Create the PR on GitHub ```typescript githubClient.createPr(branch, title, body, baseBranch) ``` **Title** — built from the repo's template (`prTitleTemplate` in `repos.json`): ``` [SQDP-3480] | Login button not working ``` **Body** — includes: - Agent summary (first 2000 chars of output) - Link to the Jira ticket - Flow section (Regular / Hotfix / Bugfix with checkboxes) - Build and automated test status A 2-second delay is added before creating the PR to give GitHub time to see the branch after the push. ### 6.6 Labels If the repo has `prLabels` configured in `repos.json`, labels are added based on the ticket flow: | Flow | Labels | |---|---| | `hotfix` | `prLabels.hotfix` → `prLabels.bugfix` → `[]` | | `bugfix` | `prLabels.bugfix` → `[]` | | `regular` | `prLabels.default` → `[]` | --- ## 7. Result The pipeline returns `success: true` with the list of created PRs. If no repo produced a PR (all failed build or safety checks), it returns `success: false`. Repo branch cleanup (`stale_branches_cleaned`) happens in the PR comment pipeline, not here. --- ## 8. Reference logs A full successful cycle looks like this: ``` poller_started → Jira poller started bot_account_resolved_from_email → bot accountId resolved from `JIRA_EMAIL` job_received → ticket enqueued pipeline_started → pipeline started ticket_moved_to_in_progress → Jira transition all_repos_synced → repos ready with fix/... branch rules_symlinked → remote rules linked (if applicable) downloading_attachments → ticket images triage_completed verdict=PROCEED → triage passed cursor_cli_started (triage) → triage agent running cursor_cli_started (fix) → fix agent running cursor_cli_finished → agent finished changes_detected → repos with changes detected diff_stats → filesChanged, linesChanged committed_and_pushed → fix committed build_started / build_passed → build validation pr_created → PR opened on GitHub pipeline_completed → success job_completed ``` --- ## 9. Abort conditions | Condition | Jira comment | Ticket status | |---|---|---| | No repos synced | No | Unchanged | | Triage → NEEDS_INFO | Yes (analysis + questions) | Returns to "To Do" | | Fix agent fails (exit ≠ 0) | Yes (agent summary) | Stays "In Progress" | | Fix agent produces no changes | Yes ("could not produce a fix") | Stays "In Progress" | | Build fails after retry | No (per repo) | Stays "In Progress" | | Safety check fails | No (per repo) | Stays "In Progress" | | All repos failed | No | Stays "In Progress" |