@kud/gh 0.12.3 → 0.14.0

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.
package/dist/inbox.d.ts CHANGED
@@ -109,7 +109,58 @@ export type InboxQueryOptions = {
109
109
  * reassembled.
110
110
  */
111
111
  sources?: readonly InboxSource[];
112
+ /**
113
+ * Per-source overrides for `SOURCE_LIMITS`. Anything left out keeps its
114
+ * default, so this is purely additive and a caller that passes nothing gets
115
+ * exactly the query it got before.
116
+ *
117
+ * It exists because the cap and the SHAPE are one decision and were split
118
+ * across two scopes: `shape` is a per-call option and `SOURCE_LIMITS` is a
119
+ * module constant, so "ask for a hundred rows of the cheap fragment" — the one
120
+ * combination that answers — could not be spelled from outside this file.
121
+ *
122
+ * That combination is the whole point. Measured 2026-09-08 on a live account,
123
+ * `reviewRequests` against 99 matching rows:
124
+ *
125
+ * full first: 20 11 pts ~2s 200
126
+ * full first: 50 28 pts 7.9s 200
127
+ * full first: 100 — ~11s 502, twice
128
+ * minimal first: 100 1 pt 2.1s 200
129
+ *
130
+ * The 502 is a wall-clock refusal rather than a node-count one — cutting the
131
+ * review-thread window by 65% still 502s at ~11s — so `first: 50` is the
132
+ * ceiling with a foot over the line, and a full fragment cannot reach 99 at
133
+ * any window. A two-tier fetch can: the full shape at the default cap for the
134
+ * rows that get a verdict, then `{ shape: "minimal", limits: {
135
+ * reviewRequests: 100 } }` for the rest.
136
+ *
137
+ * This note used to name the SEARCH as what was timing out, and that was
138
+ * wrong in a way worth keeping visible: it made the fix look like "address
139
+ * the rows directly and the problem goes away". It does not. A `nodes(ids:)`
140
+ * lookup with the same selections dies at the same size and the same eleven
141
+ * seconds — see `HEALTH_BATCH_SIZE`, where it is measured. What the request
142
+ * came through was never the variable; how many nodes it had to expand was.
143
+ *
144
+ * A caller doing that owes the overflow rows an honest verdict. A minimal row
145
+ * carries no health, and health absent is not health `waiting` — see
146
+ * `computeHealth`'s note on why this set is all-or-nothing, and
147
+ * `@kud/gh-workflow`'s `whoseMove`, which answers `unknown` rather than
148
+ * guessing. `healthIdsFrom` / `buildHealthQueries` / `mergeHealth` are the
149
+ * third tier that pays that debt where the caller wants the verdicts.
150
+ */
151
+ limits?: Partial<Record<InboxSource, number>>;
112
152
  };
153
+ /**
154
+ * The cap each source will actually ask for, defaults merged with the caller's
155
+ * overrides.
156
+ *
157
+ * Floored and clamped to at least 1 rather than trusted: GitHub rejects
158
+ * `first: 0` and a fractional `first` outright, so a bad number would cost a
159
+ * whole query rather than a few rows. Capped at 100, the search API's own
160
+ * maximum, for the same reason — asking for 200 fails the document, it does not
161
+ * return 200.
162
+ */
163
+ export declare const limitsFor: (options?: InboxQueryOptions) => Record<InboxSource, number>;
113
164
  export declare const buildInboxQuery: (options?: InboxQueryOptions) => string;
114
165
  /**
115
166
  * How many sources to put in one request.
@@ -191,4 +242,85 @@ export type SourceCoverage = {
191
242
  export declare const sourceCoverage: (data: any) => Partial<Record<InboxSource, SourceCoverage>>;
192
243
  /** The sources whose rows are a sample rather than the set. */
193
244
  export declare const truncatedSources: (data: any) => InboxSource[];
245
+ /**
246
+ * How many PR ids one health request may carry.
247
+ *
248
+ * The third tier's whole reason for existing, so it is a measurement rather than
249
+ * a taste. Measured 2026-09-09 on a live account with 102 matching review
250
+ * requests, asking for the real `PR_HEALTH` + `PR_CONVERSATION` selections by
251
+ * node id:
252
+ *
253
+ * nodes(ids:) 100 — ~11s 502, twice
254
+ * nodes(ids:) 50 28 pts 8.7s 200 (6,200 nodes)
255
+ * nodes(ids:) 25 14 pts 4.0s 200 (3,100 nodes)
256
+ * 4 × 25 in parallel 56 pts 6.0s wall 200 ×4
257
+ *
258
+ * WHICH CORRECTS THE NOTE ON `limits` ABOVE, and the correction is the useful
259
+ * half: the 502 at `first: 100` was read there as the SEARCH timing out at
260
+ * GitHub's proxy. It is not. A direct node lookup — no search index involved at
261
+ * all — fails identically, at the same size and the same eleven seconds. What
262
+ * dies is the resolver's wall clock expanding ~12,000 nodes of check rollups and
263
+ * review threads, whichever door the request came through. The search endpoint
264
+ * was innocent.
265
+ *
266
+ * So batching is the MECHANISM here, not an optimisation of it: a tier three
267
+ * that fired one 100-id request would reproduce the exact failure it exists to
268
+ * fix. Cost per PR is 0.56 points either way — GraphQL prices nodes, not
269
+ * requests — so splitting buys reliability at no extra budget, exactly as
270
+ * `INBOX_SOURCES_PER_QUERY` does one level up.
271
+ */
272
+ export declare const HEALTH_BATCH_SIZE = 25;
273
+ /**
274
+ * PR node ids on this data that arrived WITHOUT the health selection.
275
+ *
276
+ * The filter is load-bearing rather than tidiness. A two-tier fetch merges the
277
+ * cheap overflow after the full rows precisely so a URL collision keeps the
278
+ * full one, which means a naive "every id here" would re-fetch the rows that
279
+ * already carry a verdict — on a 102-row account that is ~11 points bought to
280
+ * learn what tier one already answered.
281
+ *
282
+ * `sources` narrows it further, and a caller should use it: only the PR sources
283
+ * can be enriched at all, and the issue sources that truncate carry no health to
284
+ * begin with.
285
+ */
286
+ export declare const healthIdsFrom: (data: any, sources?: readonly InboxSource[]) => string[];
287
+ /**
288
+ * One `nodes(ids:)` document carrying the health and conversation selections for
289
+ * PRs already identified elsewhere.
290
+ *
291
+ * BOTH fragments, and health alone would be a bug. `computeHealth`'s precedence
292
+ * reads `unresolvedThreads` out of `reviewThreads`, which lives in
293
+ * `PR_CONVERSATION` — so a health-only enrichment would report `waiting` on a PR
294
+ * with open threads, which is the silent wrong token this whole set is
295
+ * all-or-nothing to prevent, one layer down.
296
+ *
297
+ * It also costs almost nothing to include: `reviewThreads` is ~100 of the ~124
298
+ * nodes a PR drags, so the rest of the conversation rides along nearly free —
299
+ * and an enriched overflow row comes out FULLY equal to a tier-one row, with its
300
+ * last actor and activity age intact, rather than a second-class one carrying a
301
+ * token and no story behind it.
302
+ */
303
+ export declare const buildHealthQuery: (ids: readonly string[]) => string;
304
+ /**
305
+ * The same, split into documents small enough to answer — to be issued in
306
+ * parallel and handed to `mergeHealth`.
307
+ *
308
+ * Prefer this to `buildHealthQuery` for anything but a handful of ids. See
309
+ * `HEALTH_BATCH_SIZE` for why the split is the mechanism.
310
+ */
311
+ export declare const buildHealthQueries: (ids: readonly string[], batchSize?: number) => string[];
312
+ /**
313
+ * Merge tier-three health onto the rows that were fetched without it.
314
+ *
315
+ * By node id, and all-or-nothing per node — a batch that failed merges nothing,
316
+ * a node that merges nothing keeps no health keys, `healthOf` reads the absence
317
+ * and `whoseMove` answers `unknown`. The invariant holds by CONSTRUCTION rather
318
+ * than by a flag anyone has to remember to pass, which is the same reason
319
+ * `@kud/gh-workflow` tests presence instead of trusting its caller.
320
+ *
321
+ * Returns a new object; the input is not mutated, because a caller merging the
322
+ * enriched copy alongside the original relies on the original still being what
323
+ * it was.
324
+ */
325
+ export declare const mergeHealth: (data: any, parts: readonly any[]) => any;
194
326
  //# sourceMappingURL=inbox.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"inbox.d.ts","sourceRoot":"","sources":["../src/inbox.ts"],"names":[],"mappings":"AAQA;;;;;;;;;;;;;GAaG;AACH;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,YAAY,KAAK,CAAA;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CASrD,CAAA;AAED,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,SAAS,CAAA;AAE3C,0EAA0E;AAC1E,MAAM,MAAM,WAAW,GACnB,OAAO,GACP,gBAAgB,GAChB,UAAU,GACV,UAAU,GACV,YAAY,GACZ,gBAAgB,GAChB,SAAS,GACT,cAAc,CAAA;AAElB,oEAAoE;AACpE,eAAO,MAAM,aAAa,EAAE,SAAS,WAAW,EAS/C,CAAA;AAED,2CAA2C;AAC3C,MAAM,MAAM,iBAAiB,GAAG;IAC9B,2DAA2D;IAC3D,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,8DAA8D;IAC9D,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,UAAU,CAAA;IAClB;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,SAAS,WAAW,EAAE,CAAA;CACjC,CAAA;AA2SD,eAAO,MAAM,eAAe,GAAI,UAAS,iBAAsB,WAqB9D,CAAA;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,uBAAuB,IAAI,CAAA;AAExC;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,GAC5B,UAAS,iBAAiB,GAAG;IAAE,eAAe,CAAC,EAAE,MAAM,CAAA;CAAO,KAC7D,MAAM,EAYR,CAAA;AASD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,cAAc,GAAI,OAAO,GAAG,EAAE,KAAG,GA8B7C,CAAA;AAED,oEAAoE;AACpE,MAAM,MAAM,cAAc,GAAG;IAC3B,qEAAqE;IACrE,KAAK,EAAE,MAAM,CAAA;IACb,2DAA2D;IAC3D,KAAK,EAAE,MAAM,CAAA;IACb,sEAAsE;IACtE,SAAS,EAAE,OAAO,CAAA;CACnB,CAAA;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,cAAc,GACzB,MAAM,GAAG,KACR,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,CAc7C,CAAA;AAED,+DAA+D;AAC/D,eAAO,MAAM,gBAAgB,GAAI,MAAM,GAAG,KAAG,WAAW,EACmB,CAAA"}
1
+ {"version":3,"file":"inbox.d.ts","sourceRoot":"","sources":["../src/inbox.ts"],"names":[],"mappings":"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"}
package/dist/inbox.js CHANGED
@@ -109,6 +109,25 @@ export const INBOX_SOURCES = [
109
109
  "repoPRs",
110
110
  "recentlyDone",
111
111
  ];
112
+ /**
113
+ * The cap each source will actually ask for, defaults merged with the caller's
114
+ * overrides.
115
+ *
116
+ * Floored and clamped to at least 1 rather than trusted: GitHub rejects
117
+ * `first: 0` and a fractional `first` outright, so a bad number would cost a
118
+ * whole query rather than a few rows. Capped at 100, the search API's own
119
+ * maximum, for the same reason — asking for 200 fails the document, it does not
120
+ * return 200.
121
+ */
122
+ export const limitsFor = (options = {}) => {
123
+ const merged = { ...SOURCE_LIMITS };
124
+ for (const source of INBOX_SOURCES) {
125
+ const asked = options.limits?.[source];
126
+ if (typeof asked === "number" && Number.isFinite(asked))
127
+ merged[source] = Math.min(100, Math.max(1, Math.trunc(asked)));
128
+ }
129
+ return merged;
130
+ };
112
131
  // computeHealth's precedence needs reviewDecision, mergeable and the check
113
132
  // rollup. A fragment that omits them does NOT degrade gracefully — it falls
114
133
  // straight through to whatever token is left, so a PR you already sent back
@@ -185,37 +204,37 @@ const sinceDay = (days) => new Date(Date.now() - days * 86400 * 1000).toISOStrin
185
204
  * appears in the document.
186
205
  */
187
206
  const SOURCES = {
188
- myPRs: ({ scope, health, conversation, }) => ` myPRs: search(query: "${scope}is:pr is:open author:@me", type: ISSUE, first: ${SOURCE_LIMITS.myPRs}) {
207
+ myPRs: ({ scope, health, conversation }, first) => ` myPRs: search(query: "${scope}is:pr is:open author:@me", type: ISSUE, first: ${first}) {
189
208
  issueCount
190
209
  nodes { __typename ... on PullRequest {
191
- number title createdAt url headRefName isDraft
210
+ id number title createdAt url headRefName isDraft
192
211
  repository { nameWithOwner }
193
212
  author { login }
194
213
  ${health}
195
214
  ${conversation}
196
215
  }}
197
216
  }`,
198
- reviewRequests: ({ scope, health, conversation, }) => ` reviewRequests: search(query: "${scope}is:pr is:open review-requested:@me", type: ISSUE, first: ${SOURCE_LIMITS.reviewRequests}) {
217
+ reviewRequests: ({ scope, health, conversation }, first) => ` reviewRequests: search(query: "${scope}is:pr is:open review-requested:@me", type: ISSUE, first: ${first}) {
199
218
  issueCount
200
219
  nodes { __typename ... on PullRequest {
201
- number title createdAt url headRefName isDraft
220
+ id number title createdAt url headRefName isDraft
202
221
  repository { nameWithOwner }
203
222
  author { login }
204
223
  ${health}
205
224
  ${conversation}
206
225
  }}
207
226
  }`,
208
- reviewed: ({ scope, health, conversation, }) => ` reviewed: search(query: "${scope}is:pr is:open reviewed-by:@me -author:@me -review-requested:@me", type: ISSUE, first: ${SOURCE_LIMITS.reviewed}) {
227
+ reviewed: ({ scope, health, conversation }, first) => ` reviewed: search(query: "${scope}is:pr is:open reviewed-by:@me -author:@me -review-requested:@me", type: ISSUE, first: ${first}) {
209
228
  issueCount
210
229
  nodes { __typename ... on PullRequest {
211
- number title createdAt url headRefName isDraft
230
+ id number title createdAt url headRefName isDraft
212
231
  repository { nameWithOwner }
213
232
  author { login }
214
233
  ${health}
215
234
  ${conversation}
216
235
  }}
217
236
  }`,
218
- assigned: ({ scope, health, conversation, issueConversation, issueLabels, }) => ` assigned: search(query: "${scope}is:open assignee:@me", type: ISSUE, first: ${SOURCE_LIMITS.assigned}) {
237
+ assigned: ({ scope, health, conversation, issueConversation, issueLabels }, first) => ` assigned: search(query: "${scope}is:open assignee:@me", type: ISSUE, first: ${first}) {
219
238
  issueCount
220
239
  nodes {
221
240
  __typename
@@ -225,13 +244,13 @@ const SOURCES = {
225
244
  ${issueLabels}
226
245
  }
227
246
  ... on PullRequest {
228
- number title createdAt url headRefName isDraft repository { nameWithOwner } author { login }
247
+ id number title createdAt url headRefName isDraft repository { nameWithOwner } author { login }
229
248
  ${health}
230
249
  ${conversation}
231
250
  }
232
251
  }
233
252
  }`,
234
- repoIssues: ({ scope, owned, issueConversation, issueLabels, }) => ` repoIssues: search(query: "${scope}${owned}is:issue is:open archived:false", type: ISSUE, first: ${SOURCE_LIMITS.repoIssues}) {
253
+ repoIssues: ({ scope, owned, issueConversation, issueLabels }, first) => ` repoIssues: search(query: "${scope}${owned}is:issue is:open archived:false", type: ISSUE, first: ${first}) {
235
254
  issueCount
236
255
  nodes { __typename ... on Issue {
237
256
  number title createdAt url
@@ -269,7 +288,7 @@ const SOURCES = {
269
288
  * source that claims before it. Measure the overlap instead and you are
270
289
  * carrying a second fact that rots silently.
271
290
  */
272
- authoredIssues: ({ scope, issueConversation, issueLabels, }) => ` authoredIssues: search(query: "${scope}is:issue is:open author:@me -user:@me archived:false", type: ISSUE, first: ${SOURCE_LIMITS.authoredIssues}) {
291
+ authoredIssues: ({ scope, issueConversation, issueLabels }, first) => ` authoredIssues: search(query: "${scope}is:issue is:open author:@me -user:@me archived:false", type: ISSUE, first: ${first}) {
273
292
  issueCount
274
293
  nodes { ... on Issue {
275
294
  number title createdAt url
@@ -279,20 +298,20 @@ const SOURCES = {
279
298
  ${issueLabels}
280
299
  }}
281
300
  }`,
282
- repoPRs: ({ scope, owned, health, conversation, }) => ` repoPRs: search(query: "${scope}${owned}is:pr is:open -author:@me archived:false", type: ISSUE, first: ${SOURCE_LIMITS.repoPRs}) {
301
+ repoPRs: ({ scope, owned, health, conversation }, first) => ` repoPRs: search(query: "${scope}${owned}is:pr is:open -author:@me archived:false", type: ISSUE, first: ${first}) {
283
302
  issueCount
284
303
  nodes { __typename ... on PullRequest {
285
- number title createdAt url headRefName isDraft
304
+ id number title createdAt url headRefName isDraft
286
305
  repository { nameWithOwner }
287
306
  author { login }
288
307
  ${health}
289
308
  ${conversation}
290
309
  }}
291
310
  }`,
292
- recentlyDone: ({ scope, doneSince, }) => ` recentlyDone: search(query: "${scope}is:pr author:@me -is:open closed:>=${doneSince}", type: ISSUE, first: ${SOURCE_LIMITS.recentlyDone}) {
311
+ recentlyDone: ({ scope, doneSince }, first) => ` recentlyDone: search(query: "${scope}is:pr author:@me -is:open closed:>=${doneSince}", type: ISSUE, first: ${first}) {
293
312
  issueCount
294
313
  nodes { __typename ... on PullRequest {
295
- number title state isDraft createdAt mergedAt closedAt url
314
+ id number title state isDraft createdAt mergedAt closedAt url
296
315
  repository { nameWithOwner }
297
316
  }}
298
317
  }`,
@@ -343,6 +362,7 @@ const sourcesFor = (options) => {
343
362
  export const buildInboxQuery = (options = {}) => {
344
363
  const selections = selectionsFor(options);
345
364
  const sources = sourcesFor(options);
365
+ const limits = limitsFor(options);
346
366
  /*
347
367
  * `rateLimit` is free — it does not count against itself — and it is the only
348
368
  * authoritative source for what this query costs. Every estimate made about
@@ -357,7 +377,7 @@ export const buildInboxQuery = (options = {}) => {
357
377
  {
358
378
  rateLimit { cost nodeCount remaining resetAt }
359
379
  viewer { login }
360
- ${sources.map((source) => SOURCES[source](selections)).join("\n")}
380
+ ${sources.map((source) => SOURCES[source](selections, limits[source])).join("\n")}
361
381
  }
362
382
  `;
363
383
  };
@@ -472,4 +492,178 @@ export const sourceCoverage = (data) => {
472
492
  };
473
493
  /** The sources whose rows are a sample rather than the set. */
474
494
  export const truncatedSources = (data) => INBOX_SOURCES.filter((source) => sourceCoverage(data)[source]?.truncated);
495
+ /**
496
+ * How many PR ids one health request may carry.
497
+ *
498
+ * The third tier's whole reason for existing, so it is a measurement rather than
499
+ * a taste. Measured 2026-09-09 on a live account with 102 matching review
500
+ * requests, asking for the real `PR_HEALTH` + `PR_CONVERSATION` selections by
501
+ * node id:
502
+ *
503
+ * nodes(ids:) 100 — ~11s 502, twice
504
+ * nodes(ids:) 50 28 pts 8.7s 200 (6,200 nodes)
505
+ * nodes(ids:) 25 14 pts 4.0s 200 (3,100 nodes)
506
+ * 4 × 25 in parallel 56 pts 6.0s wall 200 ×4
507
+ *
508
+ * WHICH CORRECTS THE NOTE ON `limits` ABOVE, and the correction is the useful
509
+ * half: the 502 at `first: 100` was read there as the SEARCH timing out at
510
+ * GitHub's proxy. It is not. A direct node lookup — no search index involved at
511
+ * all — fails identically, at the same size and the same eleven seconds. What
512
+ * dies is the resolver's wall clock expanding ~12,000 nodes of check rollups and
513
+ * review threads, whichever door the request came through. The search endpoint
514
+ * was innocent.
515
+ *
516
+ * So batching is the MECHANISM here, not an optimisation of it: a tier three
517
+ * that fired one 100-id request would reproduce the exact failure it exists to
518
+ * fix. Cost per PR is 0.56 points either way — GraphQL prices nodes, not
519
+ * requests — so splitting buys reliability at no extra budget, exactly as
520
+ * `INBOX_SOURCES_PER_QUERY` does one level up.
521
+ */
522
+ export const HEALTH_BATCH_SIZE = 25;
523
+ /*
524
+ * A node the health fragment was never asked for.
525
+ *
526
+ * Tested on `reviewDecision` alone, and that is sound rather than sloppy: the
527
+ * selection is dropped WHOLE (see `PR_HEALTH`), so one key's absence is the
528
+ * whole set's absence. `@kud/gh-workflow`'s `hasHealthSelection` tests all three
529
+ * with `||` for a different job — it guards against a caller who built the node
530
+ * by hand — and importing it here is not available anyway, since the flow runs
531
+ * this way and not back.
532
+ */
533
+ const lacksHealth = (node) => Boolean(node) && !("reviewDecision" in node);
534
+ /*
535
+ * Rows whose verdict is already settled without the selection, and which
536
+ * therefore buy nothing by being enriched: `computeHealth` short-circuits on
537
+ * `state` and `isDraft` before it reads a single check.
538
+ *
539
+ * A COST filter, not a correctness one — merging health onto a merged PR changes
540
+ * no answer, it just spends 0.56 points to learn something already known.
541
+ */
542
+ const settledWithoutHealth = (node) => node?.state === "MERGED" || node?.state === "CLOSED" || node?.isDraft === true;
543
+ /**
544
+ * PR node ids on this data that arrived WITHOUT the health selection.
545
+ *
546
+ * The filter is load-bearing rather than tidiness. A two-tier fetch merges the
547
+ * cheap overflow after the full rows precisely so a URL collision keeps the
548
+ * full one, which means a naive "every id here" would re-fetch the rows that
549
+ * already carry a verdict — on a 102-row account that is ~11 points bought to
550
+ * learn what tier one already answered.
551
+ *
552
+ * `sources` narrows it further, and a caller should use it: only the PR sources
553
+ * can be enriched at all, and the issue sources that truncate carry no health to
554
+ * begin with.
555
+ */
556
+ export const healthIdsFrom = (data, sources) => {
557
+ const ids = [];
558
+ if (!data)
559
+ return ids;
560
+ for (const source of sources ?? INBOX_SOURCES)
561
+ for (const node of data[source]?.nodes ?? []) {
562
+ if (node?.__typename !== "PullRequest")
563
+ continue;
564
+ if (typeof node.id !== "string")
565
+ continue;
566
+ if (!lacksHealth(node) || settledWithoutHealth(node))
567
+ continue;
568
+ ids.push(node.id);
569
+ }
570
+ return [...new Set(ids)];
571
+ };
572
+ /**
573
+ * One `nodes(ids:)` document carrying the health and conversation selections for
574
+ * PRs already identified elsewhere.
575
+ *
576
+ * BOTH fragments, and health alone would be a bug. `computeHealth`'s precedence
577
+ * reads `unresolvedThreads` out of `reviewThreads`, which lives in
578
+ * `PR_CONVERSATION` — so a health-only enrichment would report `waiting` on a PR
579
+ * with open threads, which is the silent wrong token this whole set is
580
+ * all-or-nothing to prevent, one layer down.
581
+ *
582
+ * It also costs almost nothing to include: `reviewThreads` is ~100 of the ~124
583
+ * nodes a PR drags, so the rest of the conversation rides along nearly free —
584
+ * and an enriched overflow row comes out FULLY equal to a tier-one row, with its
585
+ * last actor and activity age intact, rather than a second-class one carrying a
586
+ * token and no story behind it.
587
+ */
588
+ export const buildHealthQuery = (ids) => `
589
+ {
590
+ rateLimit { cost nodeCount remaining resetAt }
591
+ nodes(ids: [${ids.map((id) => JSON.stringify(id)).join(", ")}]) {
592
+ __typename
593
+ ... on PullRequest {
594
+ id
595
+ ${PR_HEALTH}
596
+ ${PR_CONVERSATION}
597
+ }
598
+ }
599
+ }
600
+ `;
601
+ /**
602
+ * The same, split into documents small enough to answer — to be issued in
603
+ * parallel and handed to `mergeHealth`.
604
+ *
605
+ * Prefer this to `buildHealthQuery` for anything but a handful of ids. See
606
+ * `HEALTH_BATCH_SIZE` for why the split is the mechanism.
607
+ */
608
+ export const buildHealthQueries = (ids, batchSize = HEALTH_BATCH_SIZE) => {
609
+ const size = Math.max(1, Math.trunc(batchSize));
610
+ const queries = [];
611
+ for (let i = 0; i < ids.length; i += size)
612
+ queries.push(buildHealthQuery(ids.slice(i, i + size)));
613
+ return queries;
614
+ };
615
+ /*
616
+ * Every health key, and the rollup non-null.
617
+ *
618
+ * GraphQL can answer 200 with `errors` and null fields, and a node arriving with
619
+ * `reviewDecision` present but `statusCheckRollup: null` is the one shape that
620
+ * defeats the guard downstream: `hasHealthSelection` would read true on the
621
+ * first key while `computeHealth` saw zero checks and returned `waiting`. A
622
+ * confident wrong answer, through the only door left open.
623
+ *
624
+ * So the test is per NODE rather than per response. A partial answer should
625
+ * degrade to fewer verdicts, never to a wrong one.
626
+ */
627
+ const carriesHealth = (node) => Boolean(node) &&
628
+ "reviewDecision" in node &&
629
+ "mergeable" in node &&
630
+ node.statusCheckRollup != null;
631
+ /**
632
+ * Merge tier-three health onto the rows that were fetched without it.
633
+ *
634
+ * By node id, and all-or-nothing per node — a batch that failed merges nothing,
635
+ * a node that merges nothing keeps no health keys, `healthOf` reads the absence
636
+ * and `whoseMove` answers `unknown`. The invariant holds by CONSTRUCTION rather
637
+ * than by a flag anyone has to remember to pass, which is the same reason
638
+ * `@kud/gh-workflow` tests presence instead of trusting its caller.
639
+ *
640
+ * Returns a new object; the input is not mutated, because a caller merging the
641
+ * enriched copy alongside the original relies on the original still being what
642
+ * it was.
643
+ */
644
+ export const mergeHealth = (data, parts) => {
645
+ if (!data)
646
+ return data;
647
+ const byId = new Map();
648
+ for (const part of parts)
649
+ for (const node of part?.nodes ?? [])
650
+ if (typeof node?.id === "string" && carriesHealth(node))
651
+ byId.set(node.id, node);
652
+ if (byId.size === 0)
653
+ return data;
654
+ const out = { ...data };
655
+ for (const source of INBOX_SOURCES) {
656
+ const answered = data[source];
657
+ if (!answered?.nodes)
658
+ continue;
659
+ out[source] = {
660
+ ...answered,
661
+ nodes: answered.nodes.map((node) => {
662
+ const found = typeof node?.id === "string" ? byId.get(node.id) : undefined;
663
+ return found ? { ...node, ...found } : node;
664
+ }),
665
+ };
666
+ }
667
+ return out;
668
+ };
475
669
  //# sourceMappingURL=inbox.js.map
package/dist/inbox.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"inbox.js","sourceRoot":"","sources":["../src/inbox.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"inbox.js","sourceRoot":"","sources":["../src/inbox.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kud/gh",
3
- "version": "0.12.3",
3
+ "version": "0.14.0",
4
4
  "description": "Surface-agnostic GitHub core — gh CLI primitives, PR review comments, health, and webhook logic.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",