Skip to content

Commit 22705e8

Browse files
authored
feat(jira): transition originating issue across workflow as tasks progress (#572) (#605)
* feat(jira): transition originating issue across workflow as tasks progress Move the originating Jira issue through its workflow so the board reflects the task lifecycle: To Do → In Progress on start, → In Review on PR. Best-effort only — logged and swallowed on any failure, sharing the existing 401/403 auth circuit breaker, so the board never fails, blocks, or retries the task. Failed tasks and tasks with no PR leave the status unchanged. Status resolution ladder (per lifecycle point): per-project override (--status-on-start / --status-on-pr) → safe heuristic (statusCategory `indeterminate` on start, `In Review` name-match on PR) → skip with a warning. Transitions requiring a screen and empty transition lists (no Transition Issues permission) are skipped. No new OAuth scopes — read:/write:jira-work cover both the transitions GET and POST. - agent/src/jira_reactions.py: add transition_task_started / transition_pr_opened best-effort helpers; extract shared circuit-breaker accounting. - agent/src/pipeline.py: invoke helpers at the start hook and the success-path PR hook (only when a PR was opened). - cdk/src/handlers/jira-webhook-processor.ts: stamp optional jira_status_on_start / jira_status_on_pr from the project mapping into channel_metadata. - cli/src/commands/jira.ts: add optional --status-on-start / --status-on-pr to `bgagent jira map`. - docs/guides/JIRA_SETUP_GUIDE.md: Board transitions section (defaults, overrides, Transition Issues permission prerequisite) + troubleshooting. Closes #572 * fix(jira): address review — crash-safe transitions, Linear-parity selection Addresses isadeks' review on PR #605. 1. (correctness) _get_issue_transitions no longer raises AttributeError on valid-but-non-object JSON (null / bare list / scalar). Previously resp.json().get(...) could raise past the best-effort boundary, propagate out of the pipeline hook, and flip an otherwise-successful task to FAILED (worst case: after the PR opened). Now guarded with isinstance(dict). 2/3. (Linear parity) Rework transition selection to mirror the Linear reference (prompt_builder.py): - Start prefers a destination NAMED "In Progress" before the indeterminate category fallback — fixes the order-dependent bug where "Blocked" (also indeterminate) could be picked. The category fallback now excludes "Blocked". - PR prefers "In Review" then synonyms (Code Review / Review / Peer Review / Reviewing) then falls back to "In Progress", so a stock board or a Code-Review column isn't silently skipped. - Skip if the issue is already at/past the target category (won't drag a card backward on re-trigger). Fetches ?fields=status&expand=transitions to get current status + transitions in one call. Overrides bypass the already-past skip (deliberate instruction). 4. (hardening) CLI trims --status-on-start/--status-on-pr and treats whitespace-only as unset, so a fat-fingered blank can't persist a permanent no-op. Tests: agent 43 (adds Blocked-vs-InProgress order, already-past skips, PR synonym + In-Progress fallback, non-dict JSON parametrized); CLI adds trim + whitespace-unset. Docs: resolution ladder rewritten to match. Full build green.
1 parent 8703f55 commit 22705e8

9 files changed

Lines changed: 1152 additions & 30 deletions

File tree

agent/src/jira_reactions.py

Lines changed: 400 additions & 26 deletions
Large diffs are not rendered by default.

agent/src/pipeline.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,11 @@
2323
resolve_linear_api_token,
2424
)
2525
from context import assemble_prompt, fetch_github_issue
26-
from jira_reactions import comment_task_started
26+
from jira_reactions import (
27+
comment_task_started,
28+
transition_pr_opened,
29+
transition_task_started,
30+
)
2731
from linear_reactions import react_task_finished, react_task_started
2832
from models import AgentResult, HydratedContext, RepoSetup, TaskConfig, TaskResult
2933
from observability import current_otel_trace_id, task_span
@@ -862,6 +866,14 @@ def _on_trace_truncated(max_bytes: int, first_dropped: int) -> None:
862866
config.channel_metadata,
863867
)
864868

869+
# Move the Jira card To Do → In Progress so the board reflects that
870+
# work has started (issue #572). No-op for non-Jira tasks.
871+
# Best-effort; failures are logged and never block the pipeline.
872+
transition_task_started(
873+
config.channel_source,
874+
config.channel_metadata,
875+
)
876+
865877
# Download attachments from S3 (version-pinned, integrity-verified)
866878
prepared_attachments: list = []
867879
if config.attachments:
@@ -1057,6 +1069,14 @@ def _on_trace_truncated(max_bytes: int, first_dropped: int) -> None:
10571069
post_span.set_attribute("pr.url", pr_url or "")
10581070
if pr_url:
10591071
progress.write_agent_milestone("pr_created", pr_url)
1072+
# Move the Jira card In Progress → In Review now that a PR is
1073+
# open (issue #572). Only fires when a PR was actually opened —
1074+
# failed / no-PR tasks leave the card where humans can see the
1075+
# failure comment. No-op for non-Jira tasks; best-effort.
1076+
transition_pr_opened(
1077+
config.channel_source,
1078+
config.channel_metadata,
1079+
)
10601080

10611081
# Memory write — capture task episode and repo learnings
10621082
memory_written = False

0 commit comments

Comments
 (0)