Issue sprint/iteration is never surfaced: Azure's IterationPath is commented out and Jira's sprints are dropped in fromProviderIssue

Author: julianmesa-gitkrakenCreated Sep 16, 2026Updated Sep 17, 2026

Summary

An issue's sprint/iteration never reaches a consumer of @gitkraken/core-gitlens. Two separate gaps: Azure's System.IterationPath is commented out of the WorkItem model, and the sprints array that @gitkraken/provider-apis already populates for Jira is silently dropped by fromProviderIssue.

This is the middle layer of three needed for a sprint filter in Kepler (gitkraken/kepler#2272). It is blocked by the provider-apis side (GKDEV-3614) for Azure, but the Jira half can ship independently — that data is already there and being thrown away.

Current state (verified on core, v0.6.0)

Azure

  • 'System.IterationPath': string; is present but commented out in WorkItem.fieldspackages/core/src/plus/integrations/providers/azure/models.ts:133.
  • Azure's account-wide issue read goes through fromProviderIssue (azureDevOps.ts:1078), so once provider-apis exposes the value it only needs plumbing here, not a new request.

Jira (the free win)

  • provider-apis already returns sprints?: IssueSprint[] on its exported Issue, populated by normalizeIssue in jiraHelpers.ts — with id, name, isActive, and start/end/completed dates.
  • fromProviderIssue (packages/core/src/plus/integrations/providers/models.ts:1711) never reads issue.sprints. The whole array is discarded at the mapper.

The model

  • Neither IssueShape (packages/core/src/git/models/issue.ts:164) nor the Issue class (:180) has any sprint or iteration field.

Scope

Carry the sprint/iteration from the provider-apis Issue onto IssueShape / Issue, and populate it in fromProviderIssue. Read-only plumbing, no new requests, no change to any query.

Decision

The shape was an open question across this issue and GKDEV-3614. It is now settled, in both places, as follows.

Upstream (GKDEV-3614): Azure gets its own field, sprints is untouched

Reusing provider-apis' IssueSprint for Azure would be a silent behaviour change in a shipped product. gitkraken.dev's Launchpad renders a Sprint filter off that exact field (src/features/launchpad/components/shared/filters/sprint-filter.tsx), collecting sprints with a structural if ('sprints' in issue) check, keying on sprint.id, sorting on startDate, and using isActive to pick its default selection. Azure has none of those three values, so a synthesized isActive would silently move every Azure item in or out of that default. provider-apis therefore adds iteration?: { path, name } and leaves sprints alone.

Here: one normalized iterations array, with honestly-optional metadata

Two upstream shapes (Jira's rich object, Azure's path) should not become two fields that every consumer has to handle separately. Normalize once, here, into the axis both providers actually share — an identity and a name — and keep the richer metadata as optional fields that are absent when the provider does not report them.

In packages/core/src/git/models/issue.ts:

typescript
/**
 * A sprint/iteration an issue belongs to.
 *
 * `isActive` and the dates are optional because only some providers report them:
 * Jira does, Azure does not. Absent means "the provider did not say", which is NOT
 * the same as `false` — a consumer defaulting to the active sprints must be able to
 * tell "not active" from "unknown", or it will silently drop every Azure item.
 */
export interface IssueIteration {
  /** Stable within the provider's project: Jira's sprint id, Azure's iteration path. */
  id: string;
  /** What the team calls it. Jira's sprint name, the leaf segment of Azure's path. */
  name: string;
  isActive?: boolean;
  startDate?: Date;
  endDate?: Date;
}

and on IssueShape:

typescript
/**
 * The sprint(s) this issue is in. An array because Jira can carry an item across
 * several; Azure reports exactly one, so a single entry there means one iteration,
 * not a truncation.
 */
iterations?: IssueIteration[];

Mapping in fromProviderIssue

  • Jira — from the existing issue.sprints: id, name, isActive and the dates map straight across (drop completedDate, which no consumer here needs; add it later if one does).
  • Azure — from issue.iteration once GKDEV-3614 ships: id is the path (unique within the project), name is the leaf segment. isActive and the dates stay absent.
  • Absent stays absent: an issue with no sprint gets no iterations at all, not [].

Rationale for optional-over-fabricated is the one already documented on IssueMember.name in this repo: a value invented in a mapper is indistinguishable downstream from one the provider reported, and cannot be undone by a consumer.

Acceptance criteria

  • IssueShape / Issue expose iterations.
  • fromProviderIssue populates it for Jira from issue.sprints, and for Azure from issue.iteration once GKDEV-3614 ships.
  • Azure entries carry no isActive and no dates — verified by a test, not just by inspection.
  • An issue with no sprint carries no iterations, rather than an empty array.
  • Uncomment 'System.IterationPath' in the Azure WorkItem model if that path ends up being the one used.
  • Released so Kepler can consume it.

Depends on

Blocks

  • gitkraken/kepler#2272 — Azure DevOps: multi-select filter on Iteration (sprint)

Context

Requested by a user through Kepler in-app feedback: they work in sprints on Azure DevOps and want to filter their board by Iteration. There is an internal ask to cover the other providers too, which is what makes the dropped Jira sprints worth fixing in the same change.

Source: gitkraken/vscode-gitlens