@gitkraken/core-gitlens 0.9.3 → 0.9.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (160) hide show
  1. package/CHANGELOG.md +34 -1
  2. package/dist/git/models/issue.d.ts +25 -2
  3. package/dist/git/models/issue.js +3 -1
  4. package/dist/git/models/issue.js.map +1 -1
  5. package/dist/git/models/pullRequest.d.ts +55 -5
  6. package/dist/git/models/pullRequest.js +5 -1
  7. package/dist/git/models/pullRequest.js.map +1 -1
  8. package/dist/git/utils/issue.utils.d.ts +4 -0
  9. package/dist/git/utils/issue.utils.js +4 -0
  10. package/dist/git/utils/issue.utils.js.map +1 -1
  11. package/dist/git/utils/pullRequest.utils.d.ts +4 -0
  12. package/dist/git/utils/pullRequest.utils.js +8 -0
  13. package/dist/git/utils/pullRequest.utils.js.map +1 -1
  14. package/dist/plus/git-github/api/github.d.ts +64 -4
  15. package/dist/plus/git-github/api/github.js +232 -15
  16. package/dist/plus/git-github/api/github.js.map +1 -1
  17. package/dist/plus/git-github/models.d.ts +48 -6
  18. package/dist/plus/git-github/models.js +11 -10
  19. package/dist/plus/git-github/models.js.map +1 -1
  20. package/dist/plus/integrations/fieldPresence.d.ts +75 -0
  21. package/dist/plus/integrations/fieldPresence.js +381 -0
  22. package/dist/plus/integrations/fieldPresence.js.map +1 -0
  23. package/dist/plus/integrations/index.d.ts +6 -4
  24. package/dist/plus/integrations/index.js +2 -1
  25. package/dist/plus/integrations/index.js.map +1 -1
  26. package/dist/plus/integrations/integrationService.d.ts +13 -4
  27. package/dist/plus/integrations/integrationService.js.map +1 -1
  28. package/dist/plus/integrations/manager.d.ts +62 -7
  29. package/dist/plus/integrations/models/gitHostIntegration.d.ts +103 -9
  30. package/dist/plus/integrations/models/gitHostIntegration.js +116 -5
  31. package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -1
  32. package/dist/plus/integrations/models/integration.d.ts +91 -4
  33. package/dist/plus/integrations/models/integration.js +56 -7
  34. package/dist/plus/integrations/models/integration.js.map +1 -1
  35. package/dist/plus/integrations/models/issueReads.d.ts +14 -0
  36. package/dist/plus/integrations/models/issuesIntegration.d.ts +44 -3
  37. package/dist/plus/integrations/models/issuesIntegration.js +51 -1
  38. package/dist/plus/integrations/models/issuesIntegration.js.map +1 -1
  39. package/dist/plus/integrations/providers/azure/azure.js +3 -3
  40. package/dist/plus/integrations/providers/azure/azure.js.map +1 -1
  41. package/dist/plus/integrations/providers/azure/models.d.ts +36 -4
  42. package/dist/plus/integrations/providers/azure/models.js +118 -8
  43. package/dist/plus/integrations/providers/azure/models.js.map +1 -1
  44. package/dist/plus/integrations/providers/azureDevOps.d.ts +102 -5
  45. package/dist/plus/integrations/providers/azureDevOps.js +264 -45
  46. package/dist/plus/integrations/providers/azureDevOps.js.map +1 -1
  47. package/dist/plus/integrations/providers/azureDevOpsServer.d.ts +30 -6
  48. package/dist/plus/integrations/providers/azureDevOpsServer.js +82 -21
  49. package/dist/plus/integrations/providers/azureDevOpsServer.js.map +1 -1
  50. package/dist/plus/integrations/providers/bitbucket/bitbucket.d.ts +21 -1
  51. package/dist/plus/integrations/providers/bitbucket/bitbucket.js +74 -10
  52. package/dist/plus/integrations/providers/bitbucket/bitbucket.js.map +1 -1
  53. package/dist/plus/integrations/providers/bitbucket/models.d.ts +45 -8
  54. package/dist/plus/integrations/providers/bitbucket/models.js +32 -9
  55. package/dist/plus/integrations/providers/bitbucket/models.js.map +1 -1
  56. package/dist/plus/integrations/providers/bitbucket-server/models.d.ts +2 -6
  57. package/dist/plus/integrations/providers/bitbucket-server/models.js +1 -3
  58. package/dist/plus/integrations/providers/bitbucket-server/models.js.map +1 -1
  59. package/dist/plus/integrations/providers/bitbucket-server/pullRequestSearch.d.ts +3 -1
  60. package/dist/plus/integrations/providers/bitbucket-server/pullRequestSearch.js +4 -1
  61. package/dist/plus/integrations/providers/bitbucket-server/pullRequestSearch.js.map +1 -1
  62. package/dist/plus/integrations/providers/bitbucket-server.d.ts +2 -2
  63. package/dist/plus/integrations/providers/bitbucket-server.js +7 -5
  64. package/dist/plus/integrations/providers/bitbucket-server.js.map +1 -1
  65. package/dist/plus/integrations/providers/bitbucket.d.ts +22 -2
  66. package/dist/plus/integrations/providers/bitbucket.js +29 -6
  67. package/dist/plus/integrations/providers/bitbucket.js.map +1 -1
  68. package/dist/plus/integrations/providers/github.d.ts +40 -3
  69. package/dist/plus/integrations/providers/github.js +133 -11
  70. package/dist/plus/integrations/providers/github.js.map +1 -1
  71. package/dist/plus/integrations/providers/gitlab/gitlab.d.ts +34 -1
  72. package/dist/plus/integrations/providers/gitlab/gitlab.js +118 -4
  73. package/dist/plus/integrations/providers/gitlab/gitlab.js.map +1 -1
  74. package/dist/plus/integrations/providers/gitlab/models.d.ts +70 -5
  75. package/dist/plus/integrations/providers/gitlab/models.js +106 -13
  76. package/dist/plus/integrations/providers/gitlab/models.js.map +1 -1
  77. package/dist/plus/integrations/providers/gitlab.d.ts +31 -2
  78. package/dist/plus/integrations/providers/gitlab.js +48 -8
  79. package/dist/plus/integrations/providers/gitlab.js.map +1 -1
  80. package/dist/plus/integrations/providers/jira-server.d.ts +3 -2
  81. package/dist/plus/integrations/providers/jira-server.js +18 -9
  82. package/dist/plus/integrations/providers/jira-server.js.map +1 -1
  83. package/dist/plus/integrations/providers/jira.d.ts +26 -2
  84. package/dist/plus/integrations/providers/jira.js +120 -9
  85. package/dist/plus/integrations/providers/jira.js.map +1 -1
  86. package/dist/plus/integrations/providers/jiraIssueByKey.d.ts +79 -0
  87. package/dist/plus/integrations/providers/jiraIssueByKey.js +58 -7
  88. package/dist/plus/integrations/providers/jiraIssueByKey.js.map +1 -1
  89. package/dist/plus/integrations/providers/linear.d.ts +19 -1
  90. package/dist/plus/integrations/providers/linear.js +105 -7
  91. package/dist/plus/integrations/providers/linear.js.map +1 -1
  92. package/dist/plus/integrations/providers/linearIssuesEtag.d.ts +42 -0
  93. package/dist/plus/integrations/providers/linearIssuesEtag.js +94 -0
  94. package/dist/plus/integrations/providers/linearIssuesEtag.js.map +1 -0
  95. package/dist/plus/integrations/providers/models.d.ts +70 -8
  96. package/dist/plus/integrations/providers/models.js +59 -21
  97. package/dist/plus/integrations/providers/models.js.map +1 -1
  98. package/dist/plus/integrations/providers/providerErrors.js +8 -1
  99. package/dist/plus/integrations/providers/providerErrors.js.map +1 -1
  100. package/dist/plus/integrations/providers/providersApi.d.ts +70 -4
  101. package/dist/plus/integrations/providers/providersApi.js +148 -4
  102. package/dist/plus/integrations/providers/providersApi.js.map +1 -1
  103. package/dist/plus/integrations/providers/pullRequestReviews.d.ts +13 -0
  104. package/dist/plus/integrations/providers/pullRequestReviews.js +30 -6
  105. package/dist/plus/integrations/providers/pullRequestReviews.js.map +1 -1
  106. package/dist/plus/integrations/providers/trello.js +11 -7
  107. package/dist/plus/integrations/providers/trello.js.map +1 -1
  108. package/dist/plus/integrations/providers/utils/providerPaging.d.ts +11 -0
  109. package/dist/plus/integrations/providers/utils/providerPaging.js +47 -1
  110. package/dist/plus/integrations/providers/utils/providerPaging.js.map +1 -1
  111. package/dist/plus/integrations/providers/utils.d.ts +1 -1
  112. package/dist/plus/integrations/reads/batchEtags.d.ts +69 -0
  113. package/dist/plus/integrations/reads/batchEtags.js +114 -0
  114. package/dist/plus/integrations/reads/batchEtags.js.map +1 -0
  115. package/dist/plus/integrations/reads/counts.js +2 -2
  116. package/dist/plus/integrations/reads/counts.js.map +1 -1
  117. package/dist/plus/integrations/reads/currentAccount.js +2 -2
  118. package/dist/plus/integrations/reads/currentAccount.js.map +1 -1
  119. package/dist/plus/integrations/reads/cursors.d.ts +8 -0
  120. package/dist/plus/integrations/reads/cursors.js +5 -3
  121. package/dist/plus/integrations/reads/cursors.js.map +1 -1
  122. package/dist/plus/integrations/reads/drains.d.ts +13 -4
  123. package/dist/plus/integrations/reads/drains.js +29 -5
  124. package/dist/plus/integrations/reads/drains.js.map +1 -1
  125. package/dist/plus/integrations/reads/etag.d.ts +26 -0
  126. package/dist/plus/integrations/reads/etag.js +108 -0
  127. package/dist/plus/integrations/reads/etag.js.map +1 -0
  128. package/dist/plus/integrations/reads/filters.d.ts +8 -0
  129. package/dist/plus/integrations/reads/filters.js +2 -1
  130. package/dist/plus/integrations/reads/filters.js.map +1 -1
  131. package/dist/plus/integrations/reads/hierarchy.js +3 -3
  132. package/dist/plus/integrations/reads/hierarchy.js.map +1 -1
  133. package/dist/plus/integrations/reads/issueBatch.d.ts +47 -16
  134. package/dist/plus/integrations/reads/issueBatch.js +55 -87
  135. package/dist/plus/integrations/reads/issueBatch.js.map +1 -1
  136. package/dist/plus/integrations/reads/issueTracker.d.ts +10 -2
  137. package/dist/plus/integrations/reads/issueTracker.js +23 -6
  138. package/dist/plus/integrations/reads/issueTracker.js.map +1 -1
  139. package/dist/plus/integrations/reads/issues.d.ts +1 -1
  140. package/dist/plus/integrations/reads/issues.js +4 -4
  141. package/dist/plus/integrations/reads/issues.js.map +1 -1
  142. package/dist/plus/integrations/reads/pullRequestBatch.d.ts +44 -6
  143. package/dist/plus/integrations/reads/pullRequestBatch.js +31 -34
  144. package/dist/plus/integrations/reads/pullRequestBatch.js.map +1 -1
  145. package/dist/plus/integrations/reads/pullRequestBranches.d.ts +5 -2
  146. package/dist/plus/integrations/reads/pullRequestBranches.js +4 -3
  147. package/dist/plus/integrations/reads/pullRequestBranches.js.map +1 -1
  148. package/dist/plus/integrations/reads/pullRequests.js +8 -4
  149. package/dist/plus/integrations/reads/pullRequests.js.map +1 -1
  150. package/dist/plus/integrations/reads/sweeps.js +8 -4
  151. package/dist/plus/integrations/reads/sweeps.js.map +1 -1
  152. package/dist/plus/integrations/reads/warnings.d.ts +23 -8
  153. package/dist/plus/integrations/reads/warnings.js +43 -19
  154. package/dist/plus/integrations/reads/warnings.js.map +1 -1
  155. package/dist/plus/integrations/results.d.ts +12 -4
  156. package/dist/plus/integrations/results.js +2 -0
  157. package/dist/plus/integrations/results.js.map +1 -1
  158. package/docs/integrations.md +389 -23
  159. package/docs/kepler-read-api-parity.md +16 -2
  160. package/package.json +2 -2
@@ -135,8 +135,8 @@ it with `page` + `hasMore` + `cursor?`. **No read throws for a provider-side fai
135
135
  | `getCurrentAccount` | `CurrentAccountResult` | Who a git host connection is signed in as; trackers refuse. |
136
136
  | `getSupportedFilters` | filter capability table | Static, connection-free. See §7. |
137
137
 
138
- A provider that cannot serve a surface says so explicitly — a warning explaining that the operation is
139
- unsupported plus `fetchFailed`, never a silent empty page. That distinction is the whole point of the result
138
+ A provider that cannot serve a surface says so explicitly — a `kind: 'unsupported'` warning (§6) plus
139
+ `fetchFailed`, never a silent empty page. That distinction is the whole point of the result
140
140
  shape: an empty `items` with no warning means "this account genuinely has nothing".
141
141
 
142
142
  `getIssuesBatch` takes the target form its provider addresses an issue by: `{ key, owner, repo, number, project? }` on
@@ -170,20 +170,93 @@ and it serves every git host. Azure DevOps also requires `project` on each targe
170
170
  targets per request; every other host costs one request per target, and GitLab a second one to confirm a miss. A
171
171
  target GitHub refuses on its own, e.g. in an org enforcing SAML SSO the token isn't authorized for, fails only that
172
172
  target. Its rows carry the same fields as the list reads' rows, except on Bitbucket Cloud — which has no single pull
173
- request read in provider-apis, so those rows come from GitLens' own REST read and lack `commentsCount`, `isDraft`
174
- and the clone URLs. On GitLab, a miss the confirming read then contradicts fails the target rather than answering
173
+ request read in provider-apis, so those rows come from GitLens' own REST read and lack `commentsCount` and the clone
174
+ URLs. On GitLab, a miss the confirming read then contradicts fails the target rather than answering
175
175
  with the confirming read's own, differently-identified row. It is uncached and bypasses the
176
176
  host's `IntegrationCacheProvider.getPullRequest`, so the caller owns caching the answer.
177
177
 
178
+ Both batch reads detect change with **etags**. Every fully read row carries an opaque `etag`, whether or not the
179
+ caller sent one; send it back as the target's `etag` on the next call. Compare etags for equality only and never
180
+ parse one: core computes it from the item's change state — a pull request's state, draft flag, update time and head
181
+ commit (plus, on Azure DevOps, a fingerprint of the fields it changes without an update time; see below); an issue's
182
+ state and update time — and it is not the provider's HTTP ETag. A field the host changes without moving the update
183
+ time is not an input unless `etagIncludes` names it. Reactions are such a field: GitHub adds a reaction without
184
+ touching the update time, so an `unchanged` issue's `thumbsUpCount` can be stale unless the call listed `'reactions'`,
185
+ and an `unchanged` pull request's always can. A row is in one of four states:
186
+
187
+ - `{ key, pullRequest | issue, etag }` — read in full.
188
+ - `{ key, unchanged: true, etag }` — a cheap check proved the caller's copy current and nothing else was fetched.
189
+ Only for a target that sent an `etag`, on a host with a cheap check.
190
+ - `{ key }` — proven absent, as before.
191
+ - no row, with `fetchFailed` and a warning — unknown, as before.
192
+
193
+ The hosts with a cheap check select only the change state:
194
+
195
+ - **GitHub/GHE:** one aliased document per 25 targets, like the full read.
196
+ - **GitLab and GitLab self-managed:** one query per project per 100 iids. That drops to 10 iids when `etagIncludes`
197
+ has `'checks'`, because GitLab resolves every job of each head pipeline for that input. GitLab resets a merge
198
+ request's merge status whenever its target branch moves, so on a busy repository `'mergeable'` changes, and costs a
199
+ full read, more often than the merge request itself does.
200
+
201
+ - **Azure DevOps and Azure DevOps Server:** work items in one batch request per project per 200 ids; an id the batch
202
+ leaves out falls through to its own full read. Pull requests can't be batched by id, so the check is one request
203
+ per pull request instead of the full read's two. Azure DevOps keeps no update time on a pull request, so its etag
204
+ also carries a revision: a 64-bit hash of the title, description, target branch and every reviewer with their vote,
205
+ read off the same response at no extra cost. It sees state, draft, closing, new pushes and those edits, including
206
+ a reviewer added or removed and an optional reviewer's vote (plus mergeability when included). It can't see a
207
+ comment or a label, which its full row doesn't carry either, or a vote that keeps the reviewer's state, such as an
208
+ approval becoming an approval with suggestions. A hash collision is the only way an edit it does see can hide,
209
+ at about 2^-64. An Azure DevOps pull request etag from before the revision existed compares unequal once and costs
210
+ one full read.
211
+ - **Bitbucket Cloud:** one list request per repository per 50 pull requests, by id and in every state, where the
212
+ full read sends one request per pull request. A pull request missing from the list is proven absent, as the full
213
+ read's 404 would prove it. Bitbucket Cloud's rows carry no mergeability or check rollup, so its etag
214
+ sees state, draft, update time and head commit, plus the participants' review decision when `'reviewDecision'` is
215
+ included; `'mergeable'` and `'checks'` add nothing and cost nothing.
216
+ - **Jira Cloud** (the tracker form of `getIssuesBatch`): one bulk fetch per site per 100 keys. Bulk fetch silently
217
+ leaves out a key it can't answer (deleted, not visible, malformed or moved), so such a key falls through to its own
218
+ full read, whose 404 is what proves an absence.
219
+ - **Linear** (the tracker form of `getIssuesBatch`): one query per team per 50 issues, archived ones included, where
220
+ the full read sends one request per issue. The query matches the team's current key, while the full read may also
221
+ resolve an identifier the issue no longer carries (its team was renamed, or it moved), so an issue the query leaves
222
+ out, or an identifier that isn't a team key and a number, falls through to its own full read, which decides whether
223
+ it is absent.
224
+
225
+ Every other host and tracker accepts the `etag`, reads the target in full, and etags the row. On a host with a cheap check, a call is up to three integration calls: the cheap check
226
+ of the targets that sent an `etag`, a full read of the rest started alongside it, and a full read of the targets
227
+ whose etag no longer matched once the check settles. A call in which no target sends an `etag` makes exactly the one
228
+ full read it always made. `getPullRequestsBatch` also takes `etagIncludes`, a list drawn from `'mergeable'`,
229
+ `'reviewDecision'` and `'checks'` (the check rollup). Each entry widens every etag to one input GitHub changes without
230
+ moving the update time — without `'checks'` a red CI run goes unnoticed — and costs its own fields in the cheap
231
+ check, so a caller lists only what it needs; on GitHub the review decision is the costliest
232
+ (GitHub computes it per pull request), the rollup next, mergeability cheapest. `getIssuesBatch` takes `etagIncludes`
233
+ too, drawn from `'reactions'`: the thumbs-up count, which costs one field in the cheap check on GitHub/GHE (the full
234
+ read's own `reactions(content: THUMBS_UP)` selection) and GitLab (`upvotes`). Azure DevOps work items, Jira and Linear
235
+ rows carry no real reaction count, so there it widens nothing and costs nothing. Order and repeats don't matter (core sorts and dedupes the list), and an unknown value refuses the whole
236
+ call before any request, like a bad target. It changes only which etag is computed (and the cheap check's
237
+ selection), and an etag from another set, or from an older scheme, simply compares unequal and costs a full read,
238
+ never a false `unchanged`. A target the cheap check fails falls through to the full read, which has the final word and supplies
239
+ the warning if it fails too — except on an `auth`, `rate-limit` or `no-connection` failure, which the full read
240
+ would only hit again: those targets are dropped with that warning and `fetchFailed`, exactly as a failed full read
241
+ drops them. Each target is judged by its own failure, so one target's rate limit never drops another the check only
242
+ failed to answer. The cheap check counts toward the integration's disconnect budget only when the credential is
243
+ refused; any other failure of it is left to the full reads, so a call counts at most twice, once per full read.
244
+
178
245
  `getPullRequestsForBranches` answers "which pull requests have this branch as their head" without the user's
179
246
  relationship to them, so unlike the sweeps it finds a teammate's pull request from the user's branch. A target names
180
247
  the repository the pull requests are opened against, the branch's short name and, for a branch in a fork,
181
248
  `headOwner`; one equal to the target's owner means the base repository. A pull request matches only when its head
182
249
  repository is that base repository or that fork, so a `main` never claims every fork's `main`. The match is on the
183
- head branch name in every state, so a merged pull request whose branch was deleted is still found. Each target
184
- returns up to 10, most recently updated first. An empty list without `truncated` is a proven none, a missing base
185
- repository included; `truncated` means more may match than were returned. GitHub/GHE answer up to 25 targets per
186
- request, and every other host costs one request per target. GitLab and Azure DevOps, where that request only finds
250
+ head branch name in every state, so a merged pull request whose branch was deleted is still found, and a fork is
251
+ matched by its owner, so a pull request whose fork was deleted still matches that `headOwner`. Each target returns up
252
+ to 10, most recently updated first. An empty list without `truncated` is a proven none, a missing base repository included; `truncated` means a
253
+ pull request that wasn't returned could still match. GitHub/GHE answer up to 25 targets per request, matching the
254
+ branch name across every fork. A target whose name more forks share than that request returned (`main` or `patch-1`
255
+ in a fork-heavy repository) costs one more REST request, which filters by head owner on the server; a match the first
256
+ request missed is resolved through `getPullRequestsBatch`'s own read, the answer is the union of both requests'
257
+ matches, and the target is `truncated` only when more than 10 truly match. The REST request can't see a pull request
258
+ from a deleted fork, so on such a target one that the first request didn't return can't be found, and may be missing
259
+ from an answer, even an empty one, that isn't `truncated`. Every other host costs one request per target. GitLab and Azure DevOps, where that request only finds
187
260
  the matching numbers, then resolve each match (typically 0–1 per branch) through `getPullRequestsBatch`'s own read,
188
261
  so their rows are exactly its rows; a match that read can't check fails its whole branch. Rows carry the list reads'
189
262
  fields on GitHub/GHE and Bitbucket DC and the by-id read's on Bitbucket Cloud. Bitbucket DC and Azure DevOps refuse a
@@ -195,6 +268,268 @@ than `items`, and `account` is never absent without a warning: no session, a fai
195
268
  which has only a per-resource account and refuses (see §8). It goes through the host-supplied
196
269
  `IntegrationManagerCacheProvider.getCurrentAccount` cache rather than adding a second one.
197
270
 
271
+ That account is not always how a pull request names the same person. Azure DevOps Server gives one person a
272
+ different identity id in each collection than at the server level, and a pull request's `author`, `reviewRequests`
273
+ and `latestReviews` carry the collection's. So the pull request reads resolve the current account per row, and every
274
+ row whose `authoredByMe` was resolved also carries `viewer`, the identity it was matched against: per collection on
275
+ Azure DevOps Server, the account itself everywhere else. To tell whether a review was requested from the current
276
+ user, or which review is theirs, compare the reviewer entries against `viewer`, not against `getCurrentAccount`. A row
277
+ whose identity couldn't be resolved has neither field, and its authorship is unknown rather than `false`.
278
+
279
+ ### Field presence
280
+
281
+ Every pull request and issue row carries `projection`, naming the read that produced it: `point` (a single-item
282
+ read), `search` and `search-summary` (the filtered search, and on the host its "my pull requests" and issue searches),
283
+ `text-search` (the host's free-text `searchPullRequests`), `account` and `account-summary` (the account-wide list and
284
+ sweeps, `account` being a sweep's `includeReviews`, and a tracker's "my issues"), `repos` and `repos-summary` (the
285
+ repository-scoped list and sweeps), `project` (a tracker project's issues: `listIssueTrackerIssuesPage`,
286
+ `getIssuesForProject`), and `batch` (`getPullRequestsBatch`, `getPullRequestsForBranches`, `getIssuesBatch`). Issues
287
+ have no `-summary` reads; `broadenIssues` rows carry the tag of the read that served each org. The tag names a read,
288
+ not a field set: the same tag can carry different fields on different providers.
289
+
290
+ Which fields a row carries depends on that read. A field it never fetched is `undefined`, but so is a fetched field that
291
+ is empty, and provider-apis' converters still fill a few placeholders (see
292
+ [Every other provider](#every-other-provider)), so neither a missing nor a present value says what the read fetched.
293
+ `getPullRequestFieldPresence(row)` and `getIssueFieldPresence(row)` answer it per field group:
294
+
295
+ - `fetched` — the read asked for it and surfaces it, so an empty value means "none".
296
+ - `not-requested` — this read didn't ask for it, or doesn't surface it faithfully. **Ignore the value even when one is
297
+ present**; another read may supply it. When merging rows from several reads, take each group from a row that
298
+ fetched it.
299
+ - `unavailable` — this host can't supply it on this read.
300
+
301
+ Both return `undefined` for an untagged row, and for a tag the row's provider never produces. The serialized copies
302
+ (`serializePullRequest`, `serializeIssue`) drop fields a table can call fetched, so they carry no tag.
303
+
304
+ A group is `fetched` only if every field in it is. Pull request groups: `description` (`body`), `reviews`
305
+ (`latestReviews`), `reviewRequests` (who is requested; each request's `isCodeOwner` is set only by GitLens' own GitHub
306
+ reads, and is `undefined` elsewhere), `reviewDecision`, `assignees`, `checks` (`statusCheckRollupState`), `mergeable`
307
+ (`mergeableState`), `diffStats` (`additions`, `deletions`, `filesChanged`), `commitCount`, `comments` (`commentsCount`),
308
+ `reactions` (`thumbsUpCount`), `access` (`viewerCanUpdate` and `repository.accessLevel`), `authoredByMe` (with `viewer`,
309
+ the identity it was matched against) and `stack`. `statusCheckRollupState`, `commitCount` and `viewerCanUpdate` live on
310
+ the `PullRequest` class every row is, not on the `PullRequestShape` type. Issue groups: `description` (`body`),
311
+ `assignees`, `labels`, `comments` (`commentsCount`), `reactions` (`thumbsUpCount`) and `access`
312
+ (`repository.accessLevel`).
313
+
314
+ Each table below covers a cloud host and its self-managed variant: where a cell reads `cloud / self-managed` the two
315
+ differ, and `—` means that host never produces the tag.
316
+
317
+ #### GitHub and GitHub Enterprise
318
+
319
+ <!-- field-presence: pull-requests -->
320
+
321
+ | Group | point | search | search-summary | text-search | account | account-summary | repos | repos-summary | batch |
322
+ | -------------- | --------------------- | --------------------- | --------------------- | --------------------- | --------------------- | --------------------- | ----------------------- | ------------- | --------------------- |
323
+ | description | fetched | fetched | fetched | fetched | fetched | fetched | fetched | fetched | fetched |
324
+ | reviews | not-requested | fetched | not-requested | fetched | fetched | not-requested | not-requested | not-requested | fetched |
325
+ | reviewRequests | not-requested | fetched | not-requested | fetched | fetched | not-requested | not-requested | not-requested | fetched |
326
+ | reviewDecision | not-requested | fetched | not-requested | fetched | fetched | not-requested | not-requested | not-requested | fetched |
327
+ | assignees | not-requested | fetched | not-requested | fetched | fetched | not-requested | fetched | fetched | fetched |
328
+ | checks | not-requested | fetched | not-requested | fetched | fetched | not-requested | fetched / not-requested | not-requested | fetched |
329
+ | mergeable | not-requested | fetched | not-requested | fetched | fetched | not-requested | fetched | fetched | fetched |
330
+ | diffStats | not-requested | fetched | not-requested | fetched | fetched | not-requested | fetched | fetched | fetched |
331
+ | commitCount | not-requested | fetched | not-requested | fetched | fetched | not-requested | fetched | not-requested | fetched |
332
+ | comments | not-requested | fetched | not-requested | fetched | fetched | not-requested | fetched | fetched | fetched |
333
+ | reactions | not-requested | not-requested | not-requested | not-requested | not-requested | not-requested | fetched | fetched | not-requested |
334
+ | access | not-requested | fetched | not-requested | fetched | fetched | not-requested | not-requested | not-requested | fetched |
335
+ | authoredByMe | not-requested | not-requested | not-requested | not-requested | not-requested | not-requested | not-requested | not-requested | not-requested |
336
+ | stack | fetched / unavailable | fetched / unavailable | fetched / unavailable | fetched / unavailable | fetched / unavailable | fetched / unavailable | not-requested | not-requested | fetched / unavailable |
337
+
338
+ <!-- field-presence: issues -->
339
+
340
+ | Group | point | search | account | repos | batch |
341
+ | ----------- | ------- | ------- | ------- | ------------- | ------- |
342
+ | description | fetched | fetched | fetched | not-requested | fetched |
343
+ | assignees | fetched | fetched | fetched | fetched | fetched |
344
+ | labels | fetched | fetched | fetched | fetched | fetched |
345
+ | comments | fetched | fetched | fetched | fetched | fetched |
346
+ | reactions | fetched | fetched | fetched | fetched | fetched |
347
+ | access | fetched | fetched | fetched | not-requested | fetched |
348
+
349
+ The cells that aren't obvious from the projection names:
350
+
351
+ - `account`, `account-summary` and `batch` rows are GitLens' own, the same rows `search` and `search-summary` return,
352
+ so they read like them. That includes `reviewDecision`: where GitHub reports none (the repository requires no
353
+ review), the row has none either, even while a review is requested.
354
+ - `repos` rows come from provider-apis, which drops dismissed reviews and code-owner review requests, derives the
355
+ review decision from what is left, and doesn't read `repository.accessLevel`. It does select reactions, which
356
+ GitLens' own reads don't. Its comment count is issue comments only, where `search` counts review
357
+ comments too. On GitHub Enterprise it reads check runs only from server 3.0 on, which the table can't see.
358
+ - `authoredByMe` needs the current account, which a read may fail to resolve, so it is never `fetched`.
359
+ - `stack` is selected only against github.com.
360
+
361
+ #### Every other provider
362
+
363
+ What holds for all of them:
364
+
365
+ - **`summary` and `includeReviews` change nothing.** Only GitHub's reads have a lighter projection, so every other
366
+ provider answers a `-summary` read, or an account-wide sweep without `includeReviews`, with the same fields as the
367
+ full one. The rows still carry the tag of the read that was asked for, and the two tags have the same cells.
368
+ - **`reviewDecision` is never `fetched`.** Only GitHub reports a review decision. Elsewhere the row's decision is
369
+ derived from its reviewers' states, which ignores the host's own approval rules (GitLab's approval rules, Azure
370
+ DevOps' branch policies, Bitbucket's merge checks), so derive one from `reviews` and `reviewRequests` instead.
371
+ - **`access` and `authoredByMe` are never `fetched`**: no other host's read sets `repository.accessLevel`, and the
372
+ current account may not resolve. No other host has stacks.
373
+ - **Substitutions.** Azure DevOps has no pull request assignees: both of its converters fill `assignees` with the
374
+ reviewers, which `reviews` and `reviewRequests` already hold, so `assignees` is `unavailable` there. Votes are not
375
+ reactions: a Jira issue's or Trello card's votes, and a Bitbucket issue's, land in `thumbsUpCount` (and read 0
376
+ where voting is off), so `reactions` is `unavailable` for them. A GitLab upvote is a thumbs-up award, so it counts.
377
+ - **Placeholders.** GitLens' own reads leave a field they didn't fetch `undefined`, but provider-apis' converters,
378
+ which GitLens doesn't own, still fill some: Bitbucket's and Bitbucket Data Center's `mergeableState` is a literal
379
+ Mergeable, GitLab turns a null count into 0, Linear's `labels` is an empty list, and an Azure DevOps work item's
380
+ `thumbsUpCount` is 0 (GitLens' own batch read of one leaves it `undefined`). Each is `not-requested` or
381
+ `unavailable` in the tables.
382
+
383
+ <!-- field-presence: gitlab-pull-requests -->
384
+
385
+ | Group | point | search | text-search | account | account-summary | repos | repos-summary | batch |
386
+ | -------------- | ------------- | ----------------------- | ------------- | ----------------------- | ----------------------- | ----------------------- | ----------------------- | ----------------------- |
387
+ | description | not-requested | fetched | not-requested | fetched | fetched | fetched | fetched | fetched |
388
+ | reviews | not-requested | fetched | not-requested | fetched | fetched | fetched | fetched | fetched |
389
+ | reviewRequests | not-requested | fetched | not-requested | fetched | fetched | fetched | fetched | fetched |
390
+ | reviewDecision | not-requested | not-requested | not-requested | not-requested | not-requested | not-requested | not-requested | not-requested |
391
+ | assignees | not-requested | fetched | not-requested | fetched | fetched | fetched | fetched | fetched |
392
+ | checks | not-requested | fetched | not-requested | fetched | fetched | fetched | fetched | fetched |
393
+ | mergeable | not-requested | fetched | not-requested | fetched | fetched | fetched | fetched | fetched |
394
+ | diffStats | not-requested | not-requested | not-requested | not-requested | not-requested | not-requested | not-requested | not-requested |
395
+ | commitCount | not-requested | not-requested | not-requested | not-requested | not-requested | not-requested | not-requested | not-requested |
396
+ | comments | not-requested | fetched / not-requested | not-requested | fetched / not-requested | fetched / not-requested | fetched / not-requested | fetched / not-requested | fetched / not-requested |
397
+ | reactions | not-requested | fetched | not-requested | fetched | fetched | fetched | fetched | fetched |
398
+ | access | not-requested | not-requested | not-requested | not-requested | not-requested | not-requested | not-requested | not-requested |
399
+ | authoredByMe | not-requested | not-requested | not-requested | not-requested | not-requested | not-requested | not-requested | not-requested |
400
+ | stack | unavailable | unavailable | unavailable | unavailable | unavailable | unavailable | unavailable | unavailable |
401
+
402
+ <!-- field-presence: gitlab-issues -->
403
+
404
+ | Group | point | account | repos | batch |
405
+ | ----------- | ----------------------- | ------------- | ----------------------- | ----------------------- |
406
+ | description | fetched | fetched | fetched | fetched |
407
+ | assignees | fetched | fetched | fetched | fetched |
408
+ | labels | fetched | fetched | fetched | fetched |
409
+ | comments | fetched / not-requested | fetched | fetched / not-requested | fetched / not-requested |
410
+ | reactions | fetched | fetched | fetched | fetched |
411
+ | access | not-requested | not-requested | not-requested | not-requested |
412
+
413
+ - The point reads and the free-text search are GitLens' own, and leave everything but the merge request's identity,
414
+ refs and dates off the row, including the description they select. Every other read is provider-apis'.
415
+ - provider-apis turns a null count into 0. GitLab's schema makes the diff stats, the commit count and the comment
416
+ count nullable, so diff stats and commit counts are never `fetched`. GitLab.com's comment count resolver answers 0
417
+ rather than null, so its comment count is; a self-managed version's isn't known to. The account-wide issue read is
418
+ REST, whose comment count is always a number.
419
+
420
+ <!-- field-presence: azure-pull-requests -->
421
+
422
+ | Group | point | search | search-summary | text-search | account | account-summary | repos | repos-summary | batch |
423
+ | -------------- | ------------- | ------------- | ----------------- | ------------- | ------------- | --------------- | ------------- | ------------- | ------------- |
424
+ | description | not-requested | not-requested | — / not-requested | not-requested | not-requested | not-requested | not-requested | not-requested | fetched |
425
+ | reviews | fetched | fetched | — / fetched | fetched | fetched | fetched | fetched | fetched | fetched |
426
+ | reviewRequests | fetched | fetched | — / fetched | fetched | fetched | fetched | fetched | fetched | fetched |
427
+ | reviewDecision | not-requested | not-requested | — / not-requested | not-requested | not-requested | not-requested | not-requested | not-requested | not-requested |
428
+ | assignees | unavailable | unavailable | — / unavailable | unavailable | unavailable | unavailable | unavailable | unavailable | unavailable |
429
+ | checks | not-requested | not-requested | — / not-requested | not-requested | not-requested | not-requested | not-requested | not-requested | not-requested |
430
+ | mergeable | fetched | fetched | — / fetched | fetched | fetched | fetched | fetched | fetched | fetched |
431
+ | diffStats | not-requested | not-requested | — / not-requested | not-requested | not-requested | not-requested | not-requested | not-requested | not-requested |
432
+ | commitCount | not-requested | not-requested | — / not-requested | not-requested | not-requested | not-requested | not-requested | not-requested | not-requested |
433
+ | comments | not-requested | not-requested | — / not-requested | not-requested | not-requested | not-requested | not-requested | not-requested | not-requested |
434
+ | reactions | unavailable | unavailable | — / unavailable | unavailable | unavailable | unavailable | unavailable | unavailable | unavailable |
435
+ | access | not-requested | not-requested | — / not-requested | not-requested | not-requested | not-requested | not-requested | not-requested | not-requested |
436
+ | authoredByMe | not-requested | not-requested | — / not-requested | not-requested | not-requested | not-requested | not-requested | not-requested | not-requested |
437
+ | stack | unavailable | unavailable | — / unavailable | unavailable | unavailable | unavailable | unavailable | unavailable | unavailable |
438
+
439
+ <!-- field-presence: azure-issues -->
440
+
441
+ | Group | point | search | account | repos | batch |
442
+ | ----------- | ------------- | ----------------- | ----------- | ----------- | ----------- |
443
+ | description | fetched | — / fetched | fetched | fetched | fetched |
444
+ | assignees | fetched | — / fetched | fetched | fetched | fetched |
445
+ | labels | not-requested | — / not-requested | fetched | fetched | fetched |
446
+ | comments | fetched | — / fetched | fetched | fetched | fetched |
447
+ | reactions | unavailable | — / unavailable | unavailable | unavailable | unavailable |
448
+ | access | unavailable | — / unavailable | unavailable | unavailable | unavailable |
449
+
450
+ - Azure truncates the description of a pull request it lists (to 400 characters), so only the batch reads, which
451
+ fetch each pull request by id, have it. GitLens' own point reads leave it off the row.
452
+ - The point read and Azure DevOps Server's work item search use GitLens' own converter, which drops the tags.
453
+ - Work items belong to a project, not a repository, so `access` is `unavailable`.
454
+
455
+ <!-- field-presence: bitbucket-pull-requests -->
456
+
457
+ | Group | point | search | search-summary | text-search | account | account-summary | repos | repos-summary | batch |
458
+ | -------------- | ----------------------- | ------------- | ----------------- | ------------- | ------------- | --------------- | ------------- | ------------- | ----------------------- |
459
+ | description | fetched | fetched | — / fetched | fetched | fetched | fetched | fetched | fetched | fetched |
460
+ | reviews | not-requested / fetched | fetched | — / fetched | fetched | fetched | fetched | fetched | fetched | fetched |
461
+ | reviewRequests | not-requested / fetched | fetched | — / fetched | fetched | fetched | fetched | fetched | fetched | fetched |
462
+ | reviewDecision | not-requested | not-requested | — / not-requested | not-requested | not-requested | not-requested | not-requested | not-requested | not-requested |
463
+ | assignees | unavailable | unavailable | — / unavailable | unavailable | unavailable | unavailable | unavailable | unavailable | unavailable |
464
+ | checks | not-requested | not-requested | — / not-requested | not-requested | not-requested | not-requested | not-requested | not-requested | not-requested |
465
+ | mergeable | not-requested | not-requested | — / not-requested | not-requested | not-requested | not-requested | not-requested | not-requested | not-requested |
466
+ | diffStats | not-requested | not-requested | — / not-requested | not-requested | not-requested | not-requested | not-requested | not-requested | not-requested |
467
+ | commitCount | not-requested | not-requested | — / not-requested | not-requested | not-requested | not-requested | not-requested | not-requested | not-requested |
468
+ | comments | not-requested / fetched | fetched | — / fetched | fetched | fetched | fetched | fetched | fetched | not-requested / fetched |
469
+ | reactions | unavailable | unavailable | — / unavailable | unavailable | unavailable | unavailable | unavailable | unavailable | unavailable |
470
+ | access | not-requested | not-requested | — / not-requested | not-requested | not-requested | not-requested | not-requested | not-requested | not-requested |
471
+ | authoredByMe | not-requested | not-requested | — / not-requested | not-requested | not-requested | not-requested | not-requested | not-requested | not-requested |
472
+ | stack | unavailable | unavailable | — / unavailable | unavailable | unavailable | unavailable | unavailable | unavailable | unavailable |
473
+
474
+ <!-- field-presence: bitbucket-issues -->
475
+
476
+ | Group | point | account |
477
+ | ----------- | ------------- | ------------- |
478
+ | description | fetched | fetched |
479
+ | assignees | fetched | fetched |
480
+ | labels | unavailable | unavailable |
481
+ | comments | not-requested | not-requested |
482
+ | reactions | unavailable | unavailable |
483
+ | access | not-requested | not-requested |
484
+
485
+ - Bitbucket Cloud's point and batch reads are GitLens' own, which drop the comment count. The point read for a commit
486
+ selects `+values.*`, which isn't known to bring the participants the reviews come from as the other point reads'
487
+ selections do, so the point reads don't promise them.
488
+ - Bitbucket Cloud's issues are its deprecated tracker, which the facade refuses; only the host's own `getIssue` and
489
+ `searchMyIssues` read them. Bitbucket Data Center has no issues.
490
+ - Bitbucket Data Center reports a pull request's comment count in its `properties`; a pull request without one there
491
+ reads as having none.
492
+
493
+ <!-- field-presence: jira-issues -->
494
+
495
+ | Group | point | account | project | batch |
496
+ | ----------- | ------------- | ------------- | ------------- | ----------------------- |
497
+ | description | fetched | fetched | fetched | fetched |
498
+ | assignees | fetched | fetched | fetched | fetched |
499
+ | labels | fetched | fetched | fetched | fetched |
500
+ | comments | not-requested | not-requested | not-requested | fetched / not-requested |
501
+ | reactions | unavailable | unavailable | unavailable | unavailable |
502
+ | access | unavailable | unavailable | unavailable | unavailable |
503
+
504
+ <!-- field-presence: linear-issues -->
505
+
506
+ | Group | point | account | project | batch |
507
+ | ----------- | ------------- | ------------- | ------------- | ------------- |
508
+ | description | fetched | fetched | fetched | fetched |
509
+ | assignees | fetched | fetched | fetched | fetched |
510
+ | labels | not-requested | not-requested | not-requested | not-requested |
511
+ | comments | not-requested | not-requested | not-requested | not-requested |
512
+ | reactions | not-requested | not-requested | not-requested | not-requested |
513
+ | access | unavailable | unavailable | unavailable | unavailable |
514
+
515
+ <!-- field-presence: trello-issues -->
516
+
517
+ | Group | point | project |
518
+ | ----------- | ------------- | ------------- |
519
+ | description | not-requested | not-requested |
520
+ | assignees | fetched | fetched |
521
+ | labels | fetched | fetched |
522
+ | comments | fetched | fetched |
523
+ | reactions | unavailable | unavailable |
524
+ | access | unavailable | unavailable |
525
+
526
+ - Tracker issues belong to no repository, so `access` is `unavailable`.
527
+ - provider-apis' Jira converter counts the comments embedded in the issue rather than reading their total, so only
528
+ Jira Cloud's batch read, which is GitLens' own by-key read, has a comment count. Jira Data Center's batch read goes
529
+ through provider-apis.
530
+ - provider-apis' Linear fragment selects no labels (it fills an empty list), comments or reactions.
531
+ - A Trello card's description is never read. Trello has no batch or account-wide read.
532
+
198
533
  ## 5. Paging
199
534
 
200
535
  Two mechanisms live behind one shape, and they are **not** interchangeable:
@@ -404,13 +739,14 @@ result** instead of rejecting the call. One provider's expired token never blank
404
739
  `ProviderWarning.kind` (also exported as `ProviderWarningKind`) carries the classifications the facade can
405
740
  prove from structured errors:
406
741
 
407
- | `kind` | Meaning | Reasonable response |
408
- | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
409
- | `auth` | Token rejected (401/403 that isn't a throttle). | Prompt to reconnect that connection. A scoped one is narrower: see `scope` below. |
410
- | `rate-limit` | Throttled (429, or a 403 whose body says so). | Back off and retry; keep the last snapshot. |
411
- | `not-found` | 404/410/422 on the requested scope. | Drop that scope; don't reconnect. |
412
- | `no-connection` | The requested `connectionId`/`domain` doesn't resolve. | Re-resolve the target or re-authenticate. |
413
- | `other` | Catch-all: unsupported input, truncation, upstream/network failure, or an unclassified error. Read `omission` before treating one as a failure. | Preserve the warning and use the result flags; do not assume it is benign or non-retryable. |
742
+ | `kind` | Meaning | Reasonable response |
743
+ | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
744
+ | `auth` | Token rejected (401/403 that isn't a throttle). | Prompt to reconnect that connection. A scoped one is narrower: see `scope` below. |
745
+ | `rate-limit` | Throttled (429, or a 403 whose body says so). | Back off and retry; keep the last snapshot. |
746
+ | `not-found` | 404/410/422 on the requested scope. | Drop that scope; don't reconnect. |
747
+ | `no-connection` | The requested `connectionId`/`domain` doesn't resolve. | Re-resolve the target or re-authenticate. |
748
+ | `unsupported` | The provider can't serve this capability: a surface, filter, sort, state, search criterion or option combination it can't express. | Hide or disable the feature for that provider; don't retry or reconnect. |
749
+ | `other` | Catch-all: malformed or contradictory input, truncation, upstream/network failure, or an unclassified error. Read `omission` before treating one as a failure. | Preserve the warning and use the result flags; do not assume it is benign or non-retryable. |
414
750
 
415
751
  `isAuth` is a convenience mirror of `kind === 'auth'`. **Collapsing `kind` into that boolean loses the
416
752
  rate-limit and not-found distinctions**, which then have to be re-derived from raw provider prose.
@@ -651,6 +987,10 @@ const filters = wanted.filter(f => capability.includes(f));
651
987
  can't name. Reported as per-criterion flags rather than a list, and **always present** — a provider with no
652
988
  filtered issue search reports empty `relationships` and all-false flags, which is the signal to hide the
653
989
  surface rather than individual chips.
990
+ - `issueStates` — whether the issue-tracker reads take a `state` other than open (`'closed'`, `'all'`). True for
991
+ Jira, Jira Data Center and Linear, which narrow server-side on every page of their drains; false for Trello,
992
+ which can only read open cards and refuses the other two (warning + `fetchFailed`). An omitted `state` reads
993
+ open issues on every tracker.
654
994
 
655
995
  It's a _capability_ table, not a recommendation: passing fewer filters than listed is fine.
656
996
 
@@ -679,6 +1019,24 @@ second is a PR you have already reviewed (so it is waiting on the author). A "ne
679
1019
  both, as separate reads. The review row itself rides along only where the full projection does — the
680
1020
  filtered search, or a sweep with `includeReviews` — not from `Reviewed` on its own.
681
1021
 
1022
+ On Azure DevOps (+ Server), `ReviewRequested` and `Assignee` also find open pull requests whose reviewer is a group the
1023
+ user is a member of: a team (which is how a branch policy usually requires a review), a security group such as
1024
+ `[project]\Contributors`, a group reached through another, or a group of another project. Azure's reviewer filter only
1025
+ matches the identity it names, so these reads ask once per organization (collection) for every group the user is in
1026
+ (`_apis/identities` with expanded membership, then `_apis/identitybatch` per 100 groups; this needs the Identity read
1027
+ scope, `vso.identity`, which the connection declares), keep that set for five minutes, and read each project's open
1028
+ pull requests once without a reviewer filter. A pull request is kept when it names the user, or names one of the user's
1029
+ groups and the user didn't write it: a group asked to review the user's own pull request isn't asking the user. A
1030
+ group's request carries `isMyGroup: true` on its `reviewRequests` (or `latestReviews`) entry, while a request to the
1031
+ user by name does not.
1032
+
1033
+ Closed and merged pull requests are every pull request a project ever had, so they are still read through Azure's
1034
+ filter for the user: a group's request is only followed while the pull request is open. The cost of an open read is the
1035
+ same number of pull request requests as before (one read of each project's open pull requests in place of the filtered
1036
+ one), plus the group read when the kept set has expired. When the groups can't be read, the reads keep what names the
1037
+ user and report the organization (collection) as a scoped failure: the account-wide read with a warning and
1038
+ `fetchFailed`, the Server filtered search with a warning and `page.truncated`, and its count as uncounted.
1039
+
682
1040
  `includeReviewRequested` is a legacy account-wide breadth option used only when no explicit `filters` are
683
1041
  supplied. It remains useful for Bitbucket Cloud, where the reviewer slice requires an expensive
684
1042
  O(workspaces × repos) fan-out; prefer `filters: [ReviewRequested]` when an exact relationship is required.
@@ -703,8 +1061,9 @@ either way), so the option is purely projection plus its page cost. What it does
703
1061
  count, since one document holds every facet: five relationships × four states is 20 full-projection selections.
704
1062
 
705
1063
  `listPullRequestsPage` has no projection switch. Scoped to `repos` it goes through the provider's repo-scoped
706
- read, and GitHub's carries `latestReviews` natively; account-wide (no `repos`) it is always the lite shape and
707
- no option opts it back in. `commitOid` is absent from both: only the full projection populates it.
1064
+ read, which on GitHub selects `latestReviews` but drops dismissed reviews (see [Field presence](#field-presence));
1065
+ account-wide (no `repos`) it is always the lite shape and no option opts it back in. `commitOid` is absent from
1066
+ both: only the full projection populates it.
708
1067
 
709
1068
  The paginated read that does carry the full projection is `searchPullRequestsPage` with
710
1069
  `criteria.relationships` on GitHub/GHE (Bitbucket Data Center also exposes the search, with its native row shape):
@@ -717,7 +1076,8 @@ reach for `includeReviews`, which only exists on the all-at-once sweep.
717
1076
  ## 8. Provider capability matrix
718
1077
 
719
1078
  Derived from the provider models and `providersMetadata`. ✓ supported · ✗ reported unsupported
720
- (warning + `fetchFailed`) · — not applicable. Self-managed variants inherit their cloud family's hooks.
1079
+ (warning + `fetchFailed`) · — not applicable. Self-managed variants inherit their cloud family's hooks unless a
1080
+ footnote says otherwise.
721
1081
 
722
1082
  | Surface | GitHub / GHE | GitLab / self-hosted | Bitbucket | Bitbucket DC | Azure DevOps (+ Server) | Jira (+ DC) | Linear | Trello |
723
1083
  | ---------------------------- | :----------: | :------------------: | :-------: | :----------: | :---------------------: | :---------: | :----: | :----: |
@@ -728,12 +1088,12 @@ Derived from the provider models and `providersMetadata`. ✓ supported · ✗ r
728
1088
  | PRs, repo-scoped | ✓ | ✓ | ✓ | ✓ | ✓ | ✗ | ✗ | ✗ |
729
1089
  | PRs, account-wide | ✓ | ✓ | ✓ | ✓ | ✓ | ✗ | ✗ | ✗ |
730
1090
  | PR `states` account-wide | ✓ | ✓ | ✓ | ✓ | ✓ | — | — | — |
731
- | `searchPullRequestsPage` | ✓ | ✗ | ✗ | ✓ | ✗ | ✗ | ✗ | ✗ |
732
- | `countPullRequests` | ✓ | ✗ | ✗ | ✓ | ✗ | ✗ | ✗ | ✗ |
1091
+ | `searchPullRequestsPage` | ✓ | ✗ | ✗ | ✓ | ✗² | ✗ | ✗ | ✗ |
1092
+ | `countPullRequests` | ✓ | ✗ | ✗ | ✓ | ✗² | ✗ | ✗ | ✗ |
733
1093
  | Issues, repo-scoped | ✓ | ✓ | ✗ | ✗ | ✓ | — | — | — |
734
1094
  | Issues, account-wide | ✓ | ✓ | ✗ | ✗ | ✓ | — | — | — |
735
- | `searchIssuesPage` | ✓ | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
736
- | `countIssues` | ✓ | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
1095
+ | `searchIssuesPage` | ✓ | ✗ | ✗ | ✗ | ✗² | ✗ | ✗ | ✗ |
1096
+ | `countIssues` | ✓ | ✗ | ✗ | ✗ | ✗² | ✗ | ✗ | ✗ |
737
1097
  | `getIssuesBatch` | ✓ | ✓ | ✗ | ✗ | ✓ | ✓ | ✓ | ✗ |
738
1098
  | `getPullRequestsBatch` | ✓ | ✓ | ✓ | ✓ | ✓ | ✗ | ✗ | ✗ |
739
1099
  | `getPullRequestsForBranches` | ✓ | ✓ | ✓ | ✓¹ | ✓¹ | ✗ | ✗ | ✗ |
@@ -763,6 +1123,8 @@ autolinks are registered for the same reason: an autolink prefix has to be the p
763
1123
  finds a branch's pull requests only through the repository the branch lives in, and an Azure DevOps fork shares its
764
1124
  organization.
765
1125
 
1126
+ ² Refused by Azure DevOps (cloud) only: Azure DevOps Server serves the filtered searches and their counts.
1127
+
766
1128
  Repo-scoped PR filters: GitHub/GHE `Author, Assignee, ReviewRequested, Mention` · GitLab `Author, Assignee,
767
1129
  ReviewRequested` · Bitbucket + Bitbucket DC `Author, ReviewRequested` · Azure `Author, Assignee,
768
1130
  ReviewRequested`.
@@ -870,6 +1232,10 @@ while GitHub, GitLab, and Linear descriptions are Markdown. Jira issues therefor
870
1232
  `bodyFormat: 'jira-wiki'`; an omitted `bodyFormat` means consumers should preserve the existing behavior and treat
871
1233
  `body` as Markdown. The `'markdown'` value is reserved for providers that need to make that format explicit.
872
1234
 
1235
+ Two exceptions carry HTML with `bodyFormat` omitted: Azure DevOps work items (`System.Description`) and Bitbucket
1236
+ Cloud issues (`content.html`). Their `bodyFormat` is left unset rather than changed, so check the provider before
1237
+ treating either as Markdown.
1238
+
873
1239
  Rendering and conversion remain consumer concerns. In particular, the Jira body is neither ADF nor converted to
874
1240
  Markdown by this package.
875
1241
 
@@ -1025,7 +1391,7 @@ and Azure DevOps (+ Server). **Not** Bitbucket Data Center. A self-managed id re
1025
1391
  2. Pick an auth strategy (§2) and verify `getConfigured()` reflects your connections.
1026
1392
  3. Thread `connectionId` through every read if you support multiple accounts per provider.
1027
1393
  4. Persist the **opaque `cursor`**, not just a page number (§5).
1028
- 5. Branch on specific `warning.kind` values, handle `other` conservatively, and gate caching on
1394
+ 5. Branch on specific `warning.kind` values (`unsupported` means hide the feature, not retry), handle `other` conservatively, and gate caching on
1029
1395
  `fetchFailed` / `page.allPages` (§6).
1030
1396
  6. Intersect repo-scoped and account-wide `filters` against their distinct `getSupportedFilters` fields (§7).
1031
1397
  7. Treat "unsupported" as a first-class outcome per provider (§8) — don't render it as an error.
@@ -232,12 +232,24 @@ provider-apis' GitLab read reports a reply that carries only GraphQL errors the
232
232
  request, so its `null` alone does not prove absence; an Azure DevOps miss is trusted only when Azure's own error
233
233
  body names the pull request, repository or project as not found, not on any other 404 or a 410. Rows take the
234
234
  list reads' conversion on every host but Bitbucket Cloud, which has no single pull request read in provider-apis
235
- and so answers from GitLens' own REST read instead, missing `commentsCount`, `isDraft` and the clone URLs. On
235
+ and so answers from GitLens' own REST read instead, missing `commentsCount` and the clone URLs. On
236
236
  GitLab, a miss the confirming read then contradicts fails the target rather than answering with the confirming
237
237
  read's own, differently-identified row. The read is uncached and does not go through
238
238
  `IntegrationCacheProvider.getPullRequest`, whose key carries no connection, so the consumer's cache is the only
239
239
  one holding the answer.
240
240
 
241
+ **Change detection.** Kepler refreshes the pull requests and issues on screen every few minutes, and most refreshes
242
+ find nothing changed, yet each re-read the full GitHub projection (2.6–3.7 s for ~50 pull requests). Both batch
243
+ reads now hand back an opaque `etag` on every fully read row; a target that sends it back is first checked
244
+ cheaply, and comes back `{ key, unchanged: true, etag }` when it still matches, so only what moved is read in full.
245
+ One call still answers everything — known and new targets mix freely — and core alone defines "changed". The cheap
246
+ check exists on GitHub/GHE, GitLab, Azure DevOps (self-managed too for both), Bitbucket Cloud, Jira Cloud and Linear; every other host reads in full and etags the row. `etagIncludes` (any of
247
+ `'mergeable'`, `'reviewDecision'`, `'checks'`) widens the etag to those inputs, which GitHub changes without moving
248
+ `updatedAt`; each costs its own fields in the cheap check, the review decision most. A cheap
249
+ check that fails never answers `unchanged`: it falls through to the full read, or, on an auth, rate-limit or
250
+ connection failure, drops its targets as a failed read does. See
251
+ [`integrations.md` §4](./integrations.md#4-the-reads).
252
+
241
253
  **Pull requests by branch.** `getPullRequestsForBranches` answers "which pull requests have this branch as their
242
254
  head, in this repository" with no relationship to the user. Before it, Kepler correlated a local branch by matching
243
255
  the account-wide sweep, which holds only pull requests the user authored, is assigned or reviews — so a teammate's
@@ -247,7 +259,9 @@ still found, and on the head repository, the base one or the `headOwner` fork, s
247
259
  fork never correlates; a `headOwner` equal to the owner means the base repository, so Kepler can pass the owner of
248
260
  the remote the branch was pushed to. Up to 10 per branch; an empty list without `truncated` is a proven none a
249
261
  consumer can cache, and a failed target is dropped with `fetchFailed`. GitHub/GHE answer up to 25 branches per
250
- request, and every other host costs one request per branch. GitLab and Azure DevOps resolve each match (typically
262
+ request, plus one REST request for each branch whose name more forks share than that request returned, so a fork's
263
+ `main` settles instead of reading `truncated` on every refresh. That REST request can't see a pull request from a
264
+ deleted fork, so on such a branch one the first request didn't return can be missing from a proven none; every other host costs one request per branch. GitLab and Azure DevOps resolve each match (typically
251
265
  0–1 per branch) through `getPullRequestsBatch`'s own read, so a pull request's `url`, and so its identity, is the
252
266
  same whichever read found it. Bitbucket DC and Azure DevOps serve base-repository branches only and refuse a
253
267
  `headOwner` naming another owner.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gitkraken/core-gitlens",
3
3
  "description": "GitLens core — shared Git / AI / GitHub primitives for internal GitKraken consumption",
4
- "version": "0.9.3",
4
+ "version": "0.9.5",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "author": {
7
7
  "name": "GitKraken",
@@ -239,7 +239,7 @@
239
239
  }
240
240
  },
241
241
  "dependencies": {
242
- "@gitkraken/provider-apis": "0.60.0",
242
+ "@gitkraken/provider-apis": "0.61.0",
243
243
  "@octokit/graphql": "9.0.4",
244
244
  "@octokit/request": "10.0.14",
245
245
  "@octokit/request-error": "7.1.1",