Skip to content

feat(jira): implement SyncCapable + new CreateIssue/UpdateFields + sdk/util/retry adoption #93

Description

@alekspetrov

Context

SaaS pre-work S0.10. Implements the #83 SyncCapable contract for Jira — the connector with real write gaps: JQL search + GetTransitions/TransitionIssueTo exist, but there is NO CreateIssue and NO general field-update method (verified 2026-07-13). Also adopts the shared retry package.

Depends on: #84

Implementation

In sdk/integrations/jira:

  1. NEW CreateIssue (POST /rest/api/3/issue) and UpdateFields (PUT /rest/api/3/issue/{key}) — plain REST, ADF or plain-text body per existing client conventions.
  2. Implement core.SyncCapable: ListUpdatedSince via JQL project = X AND updated >= "<ts>" ORDER BY updated ASC paged by startAt/maxResults; ListAll same without the filter; GetIssue; TransitionState = existing GetTransitions→TransitionIssueTo (name-based); AddComment with the <!-- pilot-op:{idemKey} --> marker (Jira preserves it in ADF); mapping: StateGroup = status category (To Do / In Progress / Done), SequenceID = PROJ-42 key.
  3. Adopt sdk/util/retry (refactor(util): generalize github retry into sdk/util/retry with typed RateLimitError/AuthError #84) for all Jira HTTP calls with per-response error classification (429/Retry-After → RateLimitError; 401/403 → AuthError).

Acceptance

  • SyncCapable conformance compile check; CreateIssue + UpdateFields against mocked REST
  • Delta JQL pagination test (multi-page startAt); status-category StateGroup mapping test
  • Retry adoption: 429 with Retry-After honored, AuthError short-circuits (no retry)
  • Existing Jira connector tests green

Refs

Learned from #94/#95 review (2026-07-13) — do NOT repeat these in the Jira impl

  1. Idempotency comment scan MUST paginate: both sibling PRs scanned only the first default comments page (~30-50) — markers beyond it get double-posted on retry. Page the comment listing (maxResults loop) or scan newest-first with an explicit cap + doc comment.
  2. Delta semantics are on-or-after: updated >= "<ts>" (this spec already says >=) — do not regress to strictly-greater; callers dedupe by NativeID. Serialize timestamps in UTC and mind JQL minute-granularity truncation: round since DOWN so boundary issues are never dropped.
  3. Write-method scope: if projectID can select a project, either thread the same scope through GetIssue/UpdateFields/TransitionState/AddComment or reject cross-project NativeIDs loudly — no silent writes to the bound project's same-numbered issue.
  4. Prefer typed errors (sdk/util/retry's RateLimitError/AuthError from refactor(util): generalize github retry into sdk/util/retry with typed RateLimitError/AuthError #84, landed) over bare fmt.Errorf for permanent-vs-transient distinction.

Metadata

Metadata

Assignees

No one assigned

    Labels

    pilotPilot AI will work on thispilot-donePilot completed this

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions