pm-github 2026.10.27 → 2026.10.29
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/CHANGELOG.md +52 -39
- package/README.md +75 -3
- package/dist/closeout.d.ts.map +1 -1
- package/dist/closeout.js +71 -30
- package/dist/closeout.js.map +1 -1
- package/dist/discussion-schema.d.ts +110 -0
- package/dist/discussion-schema.d.ts.map +1 -0
- package/dist/discussion-schema.js +41 -0
- package/dist/discussion-schema.js.map +1 -0
- package/dist/discussions.d.ts +26 -0
- package/dist/discussions.d.ts.map +1 -0
- package/dist/discussions.js +215 -0
- package/dist/discussions.js.map +1 -0
- package/dist/gate.d.ts.map +1 -1
- package/dist/gate.js +10 -4
- package/dist/gate.js.map +1 -1
- package/dist/identity.d.ts.map +1 -1
- package/dist/identity.js +2 -1
- package/dist/identity.js.map +1 -1
- package/dist/import-fidelity.d.ts +8 -3
- package/dist/import-fidelity.d.ts.map +1 -1
- package/dist/import-fidelity.js +39 -10
- package/dist/import-fidelity.js.map +1 -1
- package/dist/import-tags.d.ts +15 -0
- package/dist/import-tags.d.ts.map +1 -0
- package/dist/import-tags.js +35 -0
- package/dist/import-tags.js.map +1 -0
- package/dist/index.d.ts +52 -41
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +188 -89
- package/dist/index.js.map +1 -1
- package/dist/issue-forms.d.ts +1 -1
- package/dist/issue-forms.d.ts.map +1 -1
- package/dist/issue-forms.js +8 -5
- package/dist/issue-forms.js.map +1 -1
- package/dist/label-names.d.ts +6 -0
- package/dist/label-names.d.ts.map +1 -0
- package/dist/label-names.js +15 -0
- package/dist/label-names.js.map +1 -0
- package/dist/labels.d.ts +161 -0
- package/dist/labels.d.ts.map +1 -0
- package/dist/labels.js +235 -0
- package/dist/labels.js.map +1 -0
- package/dist/projects.d.ts +6 -4
- package/dist/projects.d.ts.map +1 -1
- package/dist/projects.js +25 -24
- package/dist/projects.js.map +1 -1
- package/dist/refresh.d.ts +2 -1
- package/dist/refresh.d.ts.map +1 -1
- package/dist/refresh.js +19 -9
- package/dist/refresh.js.map +1 -1
- package/dist/webhooks.d.ts.map +1 -1
- package/dist/webhooks.js +5 -2
- package/dist/webhooks.js.map +1 -1
- package/dist/workflow-diagnostic.d.ts +9 -0
- package/dist/workflow-diagnostic.d.ts.map +1 -0
- package/dist/workflow-diagnostic.js +62 -0
- package/dist/workflow-diagnostic.js.map +1 -0
- package/dist/workspace.d.ts +18 -0
- package/dist/workspace.d.ts.map +1 -0
- package/dist/workspace.js +39 -0
- package/dist/workspace.js.map +1 -0
- package/docs/discussion-capture-example.json +14 -0
- package/docs/discussion-capture.md +44 -0
- package/manifest.json +2 -2
- package/package.json +15 -10
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2026.10.29 - 2026-10-29
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Capture selected GitHub discussion passages as reviewed native Decisions ([pm-github-selected-discussion-decisions](https://github.com/unbraind/pm-github/blob/main/.agents/pm/features/pm-github-selected-discussion-decisions.toon))
|
|
8
|
+
|
|
9
|
+
## 2026.10.28 - 2026-10-28
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Preview exact GitHub label migration impacts (BREAKING: PM CLI 2026.10.17 minimum) ([pm-github-2e38](https://github.com/unbraind/pm-github/blob/main/.agents/pm/features/pm-github-2e38.toon))
|
|
14
|
+
|
|
3
15
|
## 2026.10.26 - 2026-10-26
|
|
4
16
|
|
|
5
17
|
### Changed
|
|
@@ -34,7 +46,6 @@
|
|
|
34
46
|
- Derive release merge wait from CI duration and refresh a safe main advance once ([pm-github-release-merge-wait](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-release-merge-wait.toon))
|
|
35
47
|
- A publish spelled through a package runner was invisible to the attestation gate, so an unattested one read as clean ([pm-github-5igz](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-5igz.toon))
|
|
36
48
|
- PM CLI filtered native Bun tests falsely report an empty run ([pm-github-2ao1](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-2ao1.toon))
|
|
37
|
-
- Track pm-changelog rendering closed historical incidents as Fixed ([pm-github-n0mm](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-n0mm.toon))
|
|
38
49
|
- Stabilize the watchdog startup probe under concurrent coverage ([pm-github-sah1](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-sah1.toon))
|
|
39
50
|
- Explain privacy matches and distinguish bare-root regex literals during gated sync ([pm-github-xhtp](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-xhtp.toon))
|
|
40
51
|
- The changelog gate stamps an untagged version with the current date, so its verdict flips every midnight with no commit ([pm-github-m8vj](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-m8vj.toon))
|
|
@@ -49,11 +60,6 @@
|
|
|
49
60
|
- Closeout changelog generation attributes new deliveries to an old package release ([pm-github-lfcf](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-lfcf.toon))
|
|
50
61
|
- The release job authenticated with a stored npm token that expired, so publishing stopped while every other gate stayed green ([pm-github-m8u2](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-m8u2.toon))
|
|
51
62
|
- A release run bumped and committed a new version every night while publishing was impossible, because the only externally-failable step runs last ([pm-github-yq1d](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-yq1d.toon))
|
|
52
|
-
- The daily release cannot build a changelog once the tracker outgrows the default output budget ([pm-github-ypi5](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-ypi5.toon))
|
|
53
|
-
|
|
54
|
-
### Security
|
|
55
|
-
|
|
56
|
-
- Gate historical identities and host-path privacy across every Git object ([pm-github-zqad](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-zqad.toon))
|
|
57
63
|
|
|
58
64
|
### Other
|
|
59
65
|
|
|
@@ -63,6 +69,11 @@
|
|
|
63
69
|
- Audit copied fleet release workflows for canonical generation and one daily publication ([pm-github-sagl](https://github.com/unbraind/pm-github/blob/main/.agents/pm/tasks/pm-github-sagl.toon))
|
|
64
70
|
- Adopt the released gated sync caller in fleet repositories ([pm-github-aiok](https://github.com/unbraind/pm-github/blob/main/.agents/pm/tasks/pm-github-aiok.toon))
|
|
65
71
|
|
|
72
|
+
### Closed
|
|
73
|
+
|
|
74
|
+
- Closed: Gate historical identities and host-path privacy across every Git object ([pm-github-zqad](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-zqad.toon))
|
|
75
|
+
- Closed: The daily release cannot build a changelog once the tracker outgrows the default output budget ([pm-github-ypi5](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-ypi5.toon))
|
|
76
|
+
|
|
66
77
|
## 2026.10.16 - 2026-10-16
|
|
67
78
|
|
|
68
79
|
### Added
|
|
@@ -97,13 +108,16 @@
|
|
|
97
108
|
### Fixed
|
|
98
109
|
|
|
99
110
|
- Bound native Bun CLI repeats of a completed public atomic import ([pm-github-hptv](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-hptv.toon))
|
|
100
|
-
- Root CI runs skip the read-only-directory comment-sync fixtures visibly instead of failing them ([pm-github-rap0](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-rap0.toon))
|
|
101
111
|
|
|
102
112
|
### Other
|
|
103
113
|
|
|
104
114
|
- Reach exact all-source coverage and include every operational script ([pm-github-9cjx](https://github.com/unbraind/pm-github/blob/main/.agents/pm/tasks/pm-github-9cjx.toon))
|
|
105
115
|
- Auto-merge green Dependabot updates and group the pm toolchain into one daily PR ([pm-github-u9df](https://github.com/unbraind/pm-github/blob/main/.agents/pm/tasks/pm-github-u9df.toon))
|
|
106
116
|
|
|
117
|
+
### Closed
|
|
118
|
+
|
|
119
|
+
- Closed: Root CI runs skip the read-only-directory comment-sync fixtures visibly instead of failing them ([pm-github-rap0](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-rap0.toon))
|
|
120
|
+
|
|
107
121
|
## 2026.9.26 - 2026-09-26
|
|
108
122
|
|
|
109
123
|
### Other
|
|
@@ -178,22 +192,24 @@
|
|
|
178
192
|
|
|
179
193
|
## 2026.8.31 - 2026-08-31
|
|
180
194
|
|
|
181
|
-
###
|
|
195
|
+
### Closed
|
|
182
196
|
|
|
183
|
-
- Pin pm-changelog 2026.8.30 before the next release ([pm-github-g9g0](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-g9g0.toon))
|
|
184
|
-
- Regenerate the changelog after the release tag was created ([pm-github-5wzr](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-5wzr.toon))
|
|
197
|
+
- Closed: Pin pm-changelog 2026.8.30 before the next release ([pm-github-g9g0](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-g9g0.toon))
|
|
198
|
+
- Closed: Regenerate the changelog after the release tag was created ([pm-github-5wzr](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-5wzr.toon))
|
|
185
199
|
|
|
186
200
|
## 2026.8.29 - 2026-08-29
|
|
187
201
|
|
|
188
202
|
### Fixed
|
|
189
203
|
|
|
190
|
-
- Validate pm CLI development dependency bump ([pm-github-j8zh](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-j8zh.toon))
|
|
191
|
-
- The publish-attestation gate misses a publish routed through an unquoted shell variable ([pm-github-tko1](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-tko1.toon))
|
|
192
|
-
- True round-trip GitHub sync: search provider, validate diagnostics, safe-by-default export, fix activation ([pm-github-9dqy](https://github.com/unbraind/pm-github/blob/main/.agents/pm/features/pm-github-9dqy.toon))
|
|
193
204
|
- A failed provenance publish silently falls back to an unattested one ([pm-github-i5b8](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-i5b8.toon))
|
|
194
205
|
- Fix release publish ordering ahead of protected main push ([pm-github-v2kt](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-v2kt.toon))
|
|
195
206
|
- BREAKING: pm-github now requires pm CLI 2026.8.20 or newer; older hosts may fail installation or runtime validation ([pm-github-iswq](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-iswq.toon))
|
|
196
207
|
|
|
208
|
+
### Closed
|
|
209
|
+
|
|
210
|
+
- Closed: Validate pm CLI development dependency bump ([pm-github-j8zh](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-j8zh.toon))
|
|
211
|
+
- Closed: The publish-attestation gate misses a publish routed through an unquoted shell variable ([pm-github-tko1](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-tko1.toon))
|
|
212
|
+
|
|
197
213
|
## 2026.8.18 - 2026-08-18
|
|
198
214
|
|
|
199
215
|
### Fixed
|
|
@@ -223,13 +239,16 @@
|
|
|
223
239
|
### Fixed
|
|
224
240
|
|
|
225
241
|
- Propagate the docstring gate entry guard fix ([pm-github-wb4q](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-wb4q.toon))
|
|
226
|
-
- Converge changelog generation and verification on replace mode ([pm-github-8f60](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-8f60.toon))
|
|
227
242
|
- The mandatory docstring gate could skip its own scan and still exit zero ([pm-github-wxob](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-wxob.toon))
|
|
228
243
|
|
|
229
244
|
### Other
|
|
230
245
|
|
|
231
246
|
- Adopt the canonical pm-ops docstring gate ([pm-github-pbxd](https://github.com/unbraind/pm-github/blob/main/.agents/pm/tasks/pm-github-pbxd.toon))
|
|
232
247
|
|
|
248
|
+
### Closed
|
|
249
|
+
|
|
250
|
+
- Closed: Converge changelog generation and verification on replace mode ([pm-github-8f60](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-8f60.toon))
|
|
251
|
+
|
|
233
252
|
## 2026.8.7 - 2026-08-07
|
|
234
253
|
|
|
235
254
|
### Other
|
|
@@ -238,14 +257,14 @@
|
|
|
238
257
|
|
|
239
258
|
## 2026.8.4 - 2026-08-04
|
|
240
259
|
|
|
241
|
-
### Fixed
|
|
242
|
-
|
|
243
|
-
- Fix terminal transitions for pm-cli 2026.8.3 close_reason enforcement ([pm-github-rwq9](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-rwq9.toon))
|
|
244
|
-
|
|
245
260
|
### Other
|
|
246
261
|
|
|
247
262
|
- Resolve pm-changelog to the release that derives release dates in UTC ([pm-github-9m6j](https://github.com/unbraind/pm-github/blob/main/.agents/pm/chores/pm-github-9m6j.toon))
|
|
248
263
|
|
|
264
|
+
### Closed
|
|
265
|
+
|
|
266
|
+
- Closed: Fix terminal transitions for pm-cli 2026.8.3 close_reason enforcement ([pm-github-rwq9](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-rwq9.toon))
|
|
267
|
+
|
|
249
268
|
## 2026.7.30 - 2026-07-30
|
|
250
269
|
|
|
251
270
|
### Other
|
|
@@ -278,38 +297,34 @@
|
|
|
278
297
|
|
|
279
298
|
## 2026.7.26 - 2026-07-26
|
|
280
299
|
|
|
281
|
-
### Fixed
|
|
282
|
-
|
|
283
|
-
- Documented install command fails: pm install github.com/unbraind/pm-github cannot resolve an entry file ([pm-github-q6ql](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-q6ql.toon))
|
|
284
|
-
|
|
285
300
|
### Other
|
|
286
301
|
|
|
287
302
|
- Enable governance duplicate-detection advisory mode and adopt pm-cli 2026.7.25 ([pm-github-suxt](https://github.com/unbraind/pm-github/blob/main/.agents/pm/chores/pm-github-suxt.toon))
|
|
288
303
|
|
|
289
|
-
|
|
304
|
+
### Closed
|
|
290
305
|
|
|
291
|
-
|
|
306
|
+
- Closed: Documented install command fails: pm install github.com/unbraind/pm-github cannot resolve an entry file ([pm-github-q6ql](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-q6ql.toon))
|
|
292
307
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
### Fixed
|
|
296
|
-
|
|
297
|
-
- Import dies with an unexplained pm list-all failed on any tracker over 1 MiB, and non-atomic --dry-run previews every issue as a create ([pm-github-tn92](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-tn92.toon))
|
|
298
|
-
- CHANGELOG omits the shipped --link-deps feature and mislabels 2026.7.23 work as Unreleased ([pm-github-px45](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-px45.toon))
|
|
308
|
+
## 2026.7.25 - 2026-07-25
|
|
299
309
|
|
|
300
310
|
### Other
|
|
301
311
|
|
|
302
312
|
- Adopt --respect-item-release in changelog scripts and bump pm-changelog to 2026.7.24 ([pm-github-w7m7](https://github.com/unbraind/pm-github/blob/main/.agents/pm/chores/pm-github-w7m7.toon))
|
|
303
313
|
|
|
314
|
+
### Closed
|
|
315
|
+
|
|
316
|
+
- Closed: Import dies with an unexplained pm list-all failed on any tracker over 1 MiB, and non-atomic --dry-run previews every issue as a create ([pm-github-tn92](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-tn92.toon))
|
|
317
|
+
- Closed: CHANGELOG omits the shipped --link-deps feature and mislabels 2026.7.23 work as Unreleased ([pm-github-px45](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-px45.toon))
|
|
318
|
+
|
|
304
319
|
## 2026.7.23 - 2026-07-23
|
|
305
320
|
|
|
306
321
|
### Added
|
|
307
322
|
|
|
308
323
|
- Import --link-deps: map GitHub issue-body dependency references (Blocked by/Depends on/Blocks \#N) into pm dependency edges + emit ordering-cycle advisory via collectNewOrderingCycleWarnings ([pm-github-hdai](https://github.com/unbraind/pm-github/blob/main/.agents/pm/features/pm-github-hdai.toon))
|
|
309
324
|
|
|
310
|
-
###
|
|
325
|
+
### Closed
|
|
311
326
|
|
|
312
|
-
- Recommend pm merge reconcile (2026.7.22) over raw history-repair in Multi-agent merge safety docs ([pm-github-wpit](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-wpit.toon))
|
|
327
|
+
- Closed: Recommend pm merge reconcile (2026.7.22) over raw history-repair in Multi-agent merge safety docs ([pm-github-wpit](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-wpit.toon))
|
|
313
328
|
|
|
314
329
|
## 2026.7.21 - 2026-07-21
|
|
315
330
|
|
|
@@ -348,11 +363,6 @@
|
|
|
348
363
|
- GitHub Projects v2 bidirectional sync (import/sync/list/fields) ([pm-github-y27r](https://github.com/unbraind/pm-github/blob/main/.agents/pm/features/pm-github-y27r.toon))
|
|
349
364
|
- Full pm ecosystem production pass for pm-github ([pm-github-9eei](https://github.com/unbraind/pm-github/blob/main/.agents/pm/features/pm-github-9eei.toon))
|
|
350
365
|
|
|
351
|
-
### Fixed
|
|
352
|
-
|
|
353
|
-
- Export --apply aborts the whole batch on the first failed item ([pm-github-q3lk](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-q3lk.toon))
|
|
354
|
-
- export --apply exits 0 even when every item fails to write ([pm-github-77wj](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-77wj.toon))
|
|
355
|
-
|
|
356
366
|
### Security
|
|
357
367
|
|
|
358
368
|
- Resolve final Projects v2 review findings ([pm-github-0g3p](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-0g3p.toon))
|
|
@@ -361,6 +371,11 @@
|
|
|
361
371
|
|
|
362
372
|
- Full-cycle hardening wave: pm-github ([pm-github-mdcw](https://github.com/unbraind/pm-github/blob/main/.agents/pm/tasks/pm-github-mdcw.toon))
|
|
363
373
|
|
|
374
|
+
### Closed
|
|
375
|
+
|
|
376
|
+
- Closed: Export --apply aborts the whole batch on the first failed item ([pm-github-q3lk](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-q3lk.toon))
|
|
377
|
+
- Closed: export --apply exits 0 even when every item fails to write ([pm-github-77wj](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-77wj.toon))
|
|
378
|
+
|
|
364
379
|
## 2026.7.11 - 2026-07-11
|
|
365
380
|
|
|
366
381
|
### Other
|
|
@@ -413,7 +428,6 @@
|
|
|
413
428
|
|
|
414
429
|
### Fixed
|
|
415
430
|
|
|
416
|
-
- True round-trip GitHub sync: search provider, validate diagnostics, safe-by-default export, fix activation ([pm-github-4elt](https://github.com/unbraind/pm-github/blob/main/.agents/pm/features/pm-github-4elt.toon))
|
|
417
431
|
- FIX: add 'preflight' to manifest capabilities (activation-breaking bug) ([pm-github-qndb](https://github.com/unbraind/pm-github/blob/main/.agents/pm/tasks/pm-github-qndb.toon))
|
|
418
432
|
|
|
419
433
|
### Other
|
|
@@ -425,7 +439,6 @@
|
|
|
425
439
|
- Export: dry-run default + upsert existing issues by provenance ([pm-github-fv0x](https://github.com/unbraind/pm-github/blob/main/.agents/pm/tasks/pm-github-fv0x.toon))
|
|
426
440
|
- github search provider (search capability) ([pm-github-5sbx](https://github.com/unbraind/pm-github/blob/main/.agents/pm/tasks/pm-github-5sbx.toon))
|
|
427
441
|
- github validate diagnostics command ([pm-github-rroh](https://github.com/unbraind/pm-github/blob/main/.agents/pm/tasks/pm-github-rroh.toon))
|
|
428
|
-
- preflight capability: validate token/gh auth + repo reachability before mutating github commands ([pm-github-jget](https://github.com/unbraind/pm-github/blob/main/.agents/pm/tasks/pm-github-jget.toon))
|
|
429
442
|
- pm github sync: push pm status -\> GitHub close/reopen, guarded by token+--repo+--dry-run ([pm-github-yted](https://github.com/unbraind/pm-github/blob/main/.agents/pm/tasks/pm-github-yted.toon))
|
|
430
443
|
- renderers capability: register 'github' output format (pm items as GitHub-issue markdown) ([pm-github-vvov](https://github.com/unbraind/pm-github/blob/main/.agents/pm/tasks/pm-github-vvov.toon))
|
|
431
444
|
- Rate-limit/backoff handling (Retry-After / X-RateLimit-Reset) + useful afterCommand hook ([pm-github-rx1q](https://github.com/unbraind/pm-github/blob/main/.agents/pm/tasks/pm-github-rx1q.toon))
|
package/README.md
CHANGED
|
@@ -273,12 +273,63 @@ There are no saved plans, leases, idempotency keys, or retries in the completion
|
|
|
273
273
|
adapter. Every call re-reads native state, and the semantic form digest is only
|
|
274
274
|
a source identifier. Imports and sync do not use this opt-in export adapter.
|
|
275
275
|
|
|
276
|
+
## Label migration impact plans
|
|
277
|
+
|
|
278
|
+
Preview an exact GitHub label rename or explicit consolidation across stored
|
|
279
|
+
provider tag occurrences, declared import filters and local export mappings:
|
|
280
|
+
|
|
281
|
+
```sh
|
|
282
|
+
pm github labels plan --input label-migration.json --limit 10 --json
|
|
283
|
+
pm github labels plan --input label-migration.json --approve <approval>
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
Start with the [JSON example](docs/label-migration.json) and
|
|
287
|
+
[SDK/CLI contract](docs/label-migrations.md). Provider node IDs stay distinct even
|
|
288
|
+
when their names normalize to the same native tag. Complete counts accompany a
|
|
289
|
+
bounded page of exact affected item/policy identities and explicit choices.
|
|
290
|
+
Checking an application plan revalidates current label IDs and SDK revisions;
|
|
291
|
+
both commands are read-only. Filters/mappings must be explicitly inventoried.
|
|
292
|
+
Provider policy names match without case (`bug`, `BUG`, `BuG`); node IDs stay
|
|
293
|
+
distinct. Exports and issue-form label composition use the same comparison,
|
|
294
|
+
keeping the first spelling of each effective provider label.
|
|
295
|
+
Provider label names use the importer's exported `githubLabelsToTags` conversion:
|
|
296
|
+
a label named `one,two` produces tags `one` and `two`. Stored native tag elements
|
|
297
|
+
use only SDK array-tag normalization: a literal `one,two` remains one tag and
|
|
298
|
+
matches neither `one` nor `two`. Tag rows retain the destination label
|
|
299
|
+
in `after` and expose its complete native expansion in `after_tags`, including
|
|
300
|
+
an empty array when the destination produces no tags. Approval checks use the
|
|
301
|
+
same conversion and refuse unresolved tag choices across every affected row.
|
|
302
|
+
|
|
303
|
+
## Selected discussion decision capture
|
|
304
|
+
|
|
305
|
+
Turn explicitly selected GitHub **issue-comment** passages into reviewable native
|
|
306
|
+
Decisions with author attribution, source revision and exact quoted spans. Start
|
|
307
|
+
with [the selection example](docs/discussion-capture-example.json), then review:
|
|
308
|
+
|
|
309
|
+
```sh
|
|
310
|
+
pm github discussion capture --input selection.json --json
|
|
311
|
+
pm github discussion capture --input selection.json --approve REVIEWED_DIGEST --json
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
Only spans carrying `marked: true` are eligible. Empty selections write nothing;
|
|
315
|
+
unmarked prose is never interpreted as a decision. Apply re-reads current source
|
|
316
|
+
and native history, refuses stale approvals, and preserves existing captures on
|
|
317
|
+
retry. Quotes remain inert JSON data. Independent invalid records have bounded
|
|
318
|
+
refusal/remedy rows. TOON and JSON expose the same compact report.
|
|
319
|
+
|
|
320
|
+
The typed SDK surface is `captureGithubDiscussion(pmRoot, input, hostSdk, options)`.
|
|
321
|
+
It uses the host-injected SDK client; no local git repository is needed. Read the
|
|
322
|
+
[contract and SDK example](docs/discussion-capture.md) for exact offsets, bounds,
|
|
323
|
+
retry semantics and the remaining optimistic read-to-create concurrency window.
|
|
324
|
+
|
|
276
325
|
## Without git
|
|
277
326
|
|
|
278
327
|
With an explicit repository, GitHub import, export, refresh, sync, project commands,
|
|
279
328
|
blocker checks, issue-form compilation/completion, closeout and webhook receive/reconcile work in a plain pm tracker
|
|
280
329
|
without a git executable.
|
|
281
330
|
`pm github identity` also works there, entirely offline and without credentials.
|
|
331
|
+
`pm github labels plan` works on read-only non-git trackers; `--catalog` supplies
|
|
332
|
+
an offline complete label snapshot, otherwise it reads current GitHub labels.
|
|
282
333
|
The provider commands still require their normal GitHub access and credentials. Privacy scanning
|
|
283
334
|
with `github gate --diff <file>` also works there. Reading a proposed tracker diff
|
|
284
335
|
with `github gate` requires a git repository; otherwise pass an explicit diff.
|
|
@@ -296,9 +347,15 @@ See [SDK adoption evidence](docs/sdk-adoption-1025.md).
|
|
|
296
347
|
|
|
297
348
|
## Revalidate GitHub blockers
|
|
298
349
|
|
|
299
|
-
**Compatibility:** pm-github
|
|
300
|
-
|
|
301
|
-
|
|
350
|
+
**Compatibility:** pm-github requires PM CLI 2026.10.17 or newer. Lossless table
|
|
351
|
+
decoding first appeared in 2026.10.9, while the transaction `match` guard used
|
|
352
|
+
by identity repair first appeared in 2026.10.17. The declared minimum must
|
|
353
|
+
support both. Older hosts refuse installation or activation through matching
|
|
354
|
+
peer/development minimum ranges and the top-level manifest floor. Development
|
|
355
|
+
keeps an exact, integrity-locked SDK resolution. CI separately installs the exact
|
|
356
|
+
minimum from npm, type-checks all production and operational SDK imports, then
|
|
357
|
+
loads and exercises the packed artifact in a non-git tracker. See the
|
|
358
|
+
[published SDK evidence](docs/sdk-minimum.md).
|
|
302
359
|
|
|
303
360
|
`pm github blockers` reads a certified complete tracker and checks every blocked
|
|
304
361
|
item, plus non-terminal items with an external GitHub `blocked_by` dependency.
|
|
@@ -832,6 +889,13 @@ for schedules, groups, commit messages and labels.
|
|
|
832
889
|
|
|
833
890
|
## Automated closeout workflow
|
|
834
891
|
|
|
892
|
+
For scheduled failures, see the [run evidence and refusal guide](docs/scheduled-workflow-refusals.md).
|
|
893
|
+
Both maintenance workflows select one actionable reason from private command
|
|
894
|
+
receipts and privacy-scan it before emitting an error annotation. Closeout
|
|
895
|
+
accepts GitHub's numeric pagination aliases only after verifying repository
|
|
896
|
+
identity; denied reads identify the endpoint and status, and close validation
|
|
897
|
+
identifies the item and required fields without exposing raw exception prose.
|
|
898
|
+
|
|
835
899
|
[`.github/workflows/pm-github-closeout.yml`](.github/workflows/pm-github-closeout.yml)
|
|
836
900
|
is a reusable `workflow_call` that opens or updates **one** PR titled
|
|
837
901
|
`chore(pm): close items delivered by merged pull requests` from `pm-github/closeout`.
|
|
@@ -1113,6 +1177,14 @@ Before creating an item, import checks the complete tracker for its provenance t
|
|
|
1113
1177
|
|
|
1114
1178
|
With `--gate` and a nonempty filtered issue list, both preview and apply results include a `plan` with source dates, closure reason, release, target identity, link/create/update action and state-conflict decision. An empty filtered issue list returns without a `plan`, including with `--gate --dry-run`. GitHub wins lifecycle conflicts for GitHub-owned imports. A canceled item that GitHub later reports as completed uses the SDK's explicit repeat-closure transition with the source reason. A linked external owner keeps its local status, and the plan records `preserve-provider-owner`; `github_state` still records the source state. Atomic transaction identity includes the latest external edit or different lifecycle generation in history. Local state edits and repeated state cycles force reconciliation, while staging and compensation keep crash retries on the same journal. Finalization runs before the SDK commits and is compensated with the batch on failure. It is idempotent and runs again after a recovered atomic journal, so an interrupted finalization can be repaired by retrying the same import before the gate permits a push.
|
|
1115
1179
|
|
|
1180
|
+
An atomic apply whose complete normalized source projection already matches every item
|
|
1181
|
+
reports `unchanged: true`, zero imports/updates, and no transaction ID. It leaves
|
|
1182
|
+
native timestamps and item history untouched, including after a mixed batch of
|
|
1183
|
+
new and existing items. This shortcut is disabled while any GitHub SDK journal
|
|
1184
|
+
is unfinished; normal recovery still runs. Native annotation synchronization,
|
|
1185
|
+
dependency linking, and the post-write privacy gate still run on an unchanged
|
|
1186
|
+
item projection.
|
|
1187
|
+
|
|
1116
1188
|
SDK limitation: native `updated_at` remains the local mutation clock because the bulk SDK uses it to address rollback history. The GitHub update date is retained exactly in `github_updated_at`, and the gated plan discloses this policy. Backdating native `updated_at` safely requires an SDK import mutation whose version identity is independent of source time. Native creation, closure and completion dates still preserve GitHub lifetimes for momentum and cycle-time readers.
|
|
1117
1189
|
|
|
1118
1190
|
## Provider identity collisions
|
package/dist/closeout.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"closeout.d.ts","sourceRoot":"","sources":["../closeout.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"closeout.d.ts","sourceRoot":"","sources":["../closeout.ts"],"names":[],"mappings":"AAMA,6EAA6E;AAC7E,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACjE,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CACzC;AAED,gFAAgF;AAChF,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,cAAc,CAAC;IAC1C,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,gBAAgB,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,YAAY,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACxE,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,YAAY,CAAC;CACpB;AAED,mEAAmE;AACnE,MAAM,MAAM,YAAY,GAAG,kBAAkB,GAAG,kBAAkB,GAAG,qBAAqB,GAAG,eAAe,GACxG,cAAc,GAAG,cAAc,GAAG,MAAM,GAAG,iBAAiB,GAAG,cAAc,GAAG,cAAc,GAC9F,oBAAoB,GAAG,oBAAoB,GAAG,2BAA2B,GAAG,4BAA4B,GAAG,YAAY,GAAG,kBAAkB,CAAC;AAEjJ,2EAA2E;AAC3E,UAAU,SAAS;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,YAAY,CAAC;IACnB,IAAI,EAAE,UAAU,GAAG,UAAU,GAAG,UAAU,GAAG,aAAa,CAAC;IAC3D,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,EAAE,EAAE,OAAO,CAAC;CACb;AAED,iFAAiF;AACjF,UAAU,WAAW;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,OAAO,GAAG,IAAI,CAAC;IACvB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IACtB,IAAI,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE;YAAE,SAAS,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC;CACpD;AAgMD,oGAAoG;AACpG,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,WAAW,EAAE,QAAQ,GAAE,MAAM,EAAO,GAAG,OAAO,CAAC,SAAS,CAAC,CA2DpI;AAiBD;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,SAAK,GAAG,OAAO,CAwBrE;AAED,+FAA+F;AAC/F,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,EAAE,CAyBtG;AAkDD,4FAA4F;AAC5F,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAY5D;AAED,sHAAsH;AACtH,qBAAa,kBAAmB,SAAQ,KAAK;CAAG;AAOhD;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CAMrD;AAqBD,gFAAgF;AAChF,MAAM,MAAM,gBAAgB,GAAG,CAAC,QAAQ,EAAE,QAAQ,GAAG,UAAU,GAAG,SAAS,GAAG,WAAW,GAAG,cAAc,GAAG,QAAQ,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAExI,0FAA0F;AAC1F,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,WAAW,EAAE,QAAQ,CAAC,EAAE,gBAAgB;aAM1I,MAAM;UAAQ,MAAM;aAAW,OAAO;UAAQ,WAAW,EAAE;YAAU,SAAS,EAAE;YAAU,MAAM;YAAU,MAAM;GAJ1H;AA4PD,8FAA8F;AAC9F,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;aAzP1E,MAAM;UAAQ,MAAM;aAAW,OAAO;YAAoD,MAAM;YAAU,MAAM;;cAnenH,MAAM;YACR,MAAM;qBAEG,MAAM,EAAE;2BACF;YAAE,EAAE,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,YAAY,CAAC;YAAC,MAAM,EAAE,MAAM,CAAA;SAAE,EAAE;gBAC/D,MAAM;cACR,YAAY;;;;;EAkuBnB"}
|
package/dist/closeout.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { readGithubWorkspace, githubStatusIsTerminal } from "./workspace.js";
|
|
1
2
|
import { ownValue } from "./own-record.js";
|
|
3
|
+
import { normalizeImportValue } from "./import-fidelity.js";
|
|
2
4
|
/** Close delivered tracker items using merged pull requests and head-check evidence. */
|
|
3
|
-
import { close, hashDocument, locateItem, readLocatedItem,
|
|
5
|
+
import { close, hashDocument, locateItem, readLocatedItem, runWithActiveExtensions, setActiveExtensionHooks, listAllComplete, certifyCompleteListResult, getHistoryPath, readHistoryEntries, verifyHistoryEntries, PmCliError, EXIT_CODE } from "@unbrained/pm-cli/sdk";
|
|
4
6
|
/** Reject non-object API payloads before reading safety-critical fields. */
|
|
5
7
|
function record(value) {
|
|
6
8
|
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
@@ -41,9 +43,22 @@ function requiredContexts(value) {
|
|
|
41
43
|
throw new Error("--required-checks requires newline-separated context names.");
|
|
42
44
|
return values.flatMap(entry => entry.split("\n")).map(line => line.trim()).filter(Boolean);
|
|
43
45
|
}
|
|
46
|
+
/** A safe read diagnostic contains a known endpoint and no response or exception prose. */
|
|
47
|
+
class CloseoutReadError extends Error {
|
|
48
|
+
}
|
|
49
|
+
/** Identify a failed read without copying tokens, API bodies or transport errors. */
|
|
50
|
+
async function evidenceRead(api, url) {
|
|
51
|
+
try {
|
|
52
|
+
return await api.get(url);
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
const status = error instanceof Error ? /HTTP (\d{3})\b/.exec(error.message)?.[1] : undefined;
|
|
56
|
+
throw new CloseoutReadError(`GitHub GET ${new URL(url).pathname} failed; verify token read access and retry${status ? `: HTTP ${status}` : "."}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
44
59
|
/** Read one payload with the shared authenticated retry/redirect transport. */
|
|
45
60
|
async function payload(api, endpoint) {
|
|
46
|
-
return JSON.parse((await api
|
|
61
|
+
return JSON.parse((await evidenceRead(api, `${api.base}${endpoint}`)).body);
|
|
47
62
|
}
|
|
48
63
|
/** Drain same-endpoint pagination; reject cycles, foreign links and truncated totals. */
|
|
49
64
|
async function pages(api, endpoint, checks = false, done) {
|
|
@@ -52,12 +67,26 @@ async function pages(api, endpoint, checks = false, done) {
|
|
|
52
67
|
const seen = new Set();
|
|
53
68
|
const rows = [];
|
|
54
69
|
let total;
|
|
70
|
+
let numericPath;
|
|
55
71
|
while (next) {
|
|
56
72
|
const url = new URL(next);
|
|
57
|
-
if (url.origin !== initial.origin ||
|
|
58
|
-
throw new
|
|
73
|
+
if (url.origin !== initial.origin || seen.has(next))
|
|
74
|
+
throw new CloseoutReadError(`Unverifiable GitHub pagination for GET ${initial.pathname}; retry with a same-repository next-page link.`);
|
|
75
|
+
if (url.pathname !== initial.pathname && url.pathname !== numericPath) {
|
|
76
|
+
// GitHub emits numeric repository aliases in Link headers even when the
|
|
77
|
+
// first request used owner/name. Verify the alias before following it.
|
|
78
|
+
const named = /^\/repos\/([^/]+\/[^/]+)(\/.*)$/.exec(initial.pathname);
|
|
79
|
+
const numeric = /^\/repositories\/([1-9]\d*)(\/.*)$/.exec(url.pathname);
|
|
80
|
+
if (!named || !numeric || named[2] !== numeric[2])
|
|
81
|
+
throw new CloseoutReadError(`Unverifiable GitHub pagination for GET ${initial.pathname}; next-page endpoint changed.`);
|
|
82
|
+
const identity = record(await payload(api, `/repos/${named[1]}`));
|
|
83
|
+
if (!Number.isSafeInteger(identity.id) || String(identity.id) !== numeric[1]
|
|
84
|
+
|| text(identity.full_name).toLowerCase() !== named[1].toLowerCase())
|
|
85
|
+
throw new CloseoutReadError(`Unverifiable GitHub pagination for GET ${initial.pathname}; repository ID does not match.`);
|
|
86
|
+
numericPath = url.pathname;
|
|
87
|
+
}
|
|
59
88
|
seen.add(next);
|
|
60
|
-
const response = await api
|
|
89
|
+
const response = await evidenceRead(api, next);
|
|
61
90
|
const value = JSON.parse(response.body);
|
|
62
91
|
let page = value;
|
|
63
92
|
if (checks) {
|
|
@@ -120,20 +149,23 @@ function mergeEvidence(pr) {
|
|
|
120
149
|
return mergeEvidenceGaps(pr).length === 0;
|
|
121
150
|
}
|
|
122
151
|
/** Treat only permission and not-found responses as protection metadata gaps. */
|
|
123
|
-
async function readProtectionSource(read) {
|
|
152
|
+
async function readProtectionSource(read, gaps) {
|
|
124
153
|
try {
|
|
125
154
|
return await read();
|
|
126
155
|
}
|
|
127
156
|
catch (error) {
|
|
128
|
-
if (error instanceof Error && /HTTP (403|404)$/.test(error.message))
|
|
157
|
+
if (error instanceof Error && /HTTP (403|404)$/.test(error.message)) {
|
|
158
|
+
gaps.push(error.message);
|
|
129
159
|
return null;
|
|
160
|
+
}
|
|
130
161
|
throw error;
|
|
131
162
|
}
|
|
132
163
|
}
|
|
133
164
|
/** Combine all readable app-bound contexts without discarding known requirements. */
|
|
134
165
|
async function requirements(api, repo, branch) {
|
|
135
|
-
const
|
|
136
|
-
const
|
|
166
|
+
const gaps = [];
|
|
167
|
+
const classic = await readProtectionSource(async () => record(await payload(api, `/repos/${repo}/branches/${encodeURIComponent(branch)}/protection/required_status_checks`)), gaps);
|
|
168
|
+
const rules = await readProtectionSource(() => pages(api, `/repos/${repo}/rules/branches/${encodeURIComponent(branch)}?per_page=100`), gaps);
|
|
137
169
|
const checks = [];
|
|
138
170
|
if (classic !== null) {
|
|
139
171
|
if (!Array.isArray(classic.contexts) || !Array.isArray(classic.checks))
|
|
@@ -167,7 +199,7 @@ async function requirements(api, repo, branch) {
|
|
|
167
199
|
checks.push({ name: text(check.context), app: typeof app === "number" ? app : null });
|
|
168
200
|
}
|
|
169
201
|
}
|
|
170
|
-
return { checks, fallback: checks.length === 0, unavailable: classic === null || rules === null };
|
|
202
|
+
return { checks, fallback: checks.length === 0, unavailable: classic === null || rules === null, gaps };
|
|
171
203
|
}
|
|
172
204
|
/** Verify metadata-backed or caller-declared requirements before considering observed head runs. */
|
|
173
205
|
export async function checkCloseoutHead(api, repo, pr, declared = []) {
|
|
@@ -177,7 +209,7 @@ export async function checkCloseoutHead(api, repo, pr, declared = []) {
|
|
|
177
209
|
if (policy.unavailable) {
|
|
178
210
|
if (declared.length === 0) {
|
|
179
211
|
result.code = "required_checks_unverified";
|
|
180
|
-
result.reason =
|
|
212
|
+
result.reason = `${policy.gaps[0]}. Required-check metadata unavailable; declare --required-checks (workflow input required-checks) to verify the required contexts. GITHUB_TOKEN cannot read administration-only branch protection.`;
|
|
181
213
|
return result;
|
|
182
214
|
}
|
|
183
215
|
policy.checks.push(...declared.map(name => ({ name, app: null })));
|
|
@@ -233,14 +265,14 @@ export async function checkCloseoutHead(api, repo, pr, declared = []) {
|
|
|
233
265
|
result.code = "head_checks_passed";
|
|
234
266
|
result.ok = true;
|
|
235
267
|
}
|
|
236
|
-
catch {
|
|
268
|
+
catch (error) {
|
|
237
269
|
result.code = "head_evidence_unavailable";
|
|
270
|
+
if (error instanceof CloseoutReadError)
|
|
271
|
+
result.reason = error.message;
|
|
238
272
|
// Never echo an API body, exception payload, PR body or comment into the report.
|
|
239
273
|
}
|
|
240
274
|
return result;
|
|
241
275
|
}
|
|
242
|
-
/** Statuses that already ended an item's lifecycle. */
|
|
243
|
-
const TERMINAL_STATUSES = ["closed", "canceled"];
|
|
244
276
|
/** GitHub-style closing keywords that declare delivery of the link they directly precede. */
|
|
245
277
|
const CLOSING_KEYWORDS = new Set(["close", "closes", "closed", "fix", "fixes", "fixed", "resolve", "resolves", "resolved", "deliver", "delivers", "delivered"]);
|
|
246
278
|
/**
|
|
@@ -389,8 +421,12 @@ function closeReason(pr, passed) {
|
|
|
389
421
|
export function closeoutFailureReason(error) {
|
|
390
422
|
if (error instanceof PmCliError) {
|
|
391
423
|
const code = error.code;
|
|
392
|
-
if (typeof code === "string" && /^[a-z][a-z0-9_]{0,79}$/.test(code))
|
|
393
|
-
|
|
424
|
+
if (typeof code === "string" && /^[a-z][a-z0-9_]{0,79}$/.test(code)) {
|
|
425
|
+
const fields = [...new Set([...(error.context.recovery?.missing_required_fields ?? []), ...(error.context.field ? [error.context.field] : []),
|
|
426
|
+
...(error.context.policy_violations ?? []).flatMap(violation => violation.missing_fields)]
|
|
427
|
+
.filter(field => /^[a-z][a-z0-9_]{0,79}$/.test(field)))];
|
|
428
|
+
return `SDK close refused: ${code}.${fields.length ? ` Supply required fields: ${fields.join(", ")}.` : ""}`;
|
|
429
|
+
}
|
|
394
430
|
return `SDK close refused (exit ${error.exitCode}).`;
|
|
395
431
|
}
|
|
396
432
|
return "SDK close failed (unclassified error).";
|
|
@@ -446,7 +482,7 @@ export function runCloseout(pmRoot, repo, options, api, observer) {
|
|
|
446
482
|
return closeoutAttempt(pmRoot, repo, options, api, true, observer);
|
|
447
483
|
}
|
|
448
484
|
/** Re-select a conflicted item once; each attempt installs a lock-scoped SDK precondition. */
|
|
449
|
-
async function closeoutAttempt(
|
|
485
|
+
async function closeoutAttempt(pmPath, repo, options, api, retry, observer, onlyItem) {
|
|
450
486
|
if (!repo || !/^(?!\.{1,2}\/)[A-Za-z0-9_.-]+\/(?!\.{1,2}$)[A-Za-z0-9_.-]+$/.test(repo))
|
|
451
487
|
throw new CloseoutUsageError("closeout requires --repo owner/repo or PM_GITHUB_REPO.");
|
|
452
488
|
const numbers = usage(() => repeated(options, "pr")).map(value => {
|
|
@@ -475,10 +511,15 @@ async function closeoutAttempt(pmRoot, repo, options, api, retry, observer, only
|
|
|
475
511
|
}
|
|
476
512
|
}
|
|
477
513
|
}
|
|
514
|
+
const workspace = await readGithubWorkspace(pmPath);
|
|
515
|
+
const pmRoot = workspace.pmRoot;
|
|
478
516
|
const corpus = certifyCompleteListResult(await listAllComplete({}, { pmRoot, noExtensions: true }));
|
|
479
517
|
await observer?.("listed");
|
|
480
|
-
const settings =
|
|
481
|
-
const types =
|
|
518
|
+
const { settings, types: registry } = workspace;
|
|
519
|
+
const types = registry.type_to_folder;
|
|
520
|
+
const typeNames = new Map(Object.entries(registry.alias_to_type));
|
|
521
|
+
/** Container eligibility uses the same registry as persisted item types. */
|
|
522
|
+
const isEpic = (type) => normalizeImportValue("type", type, typeNames) === normalizeImportValue("type", "Epic", typeNames);
|
|
482
523
|
const snapshots = new Map();
|
|
483
524
|
const documents = new Map();
|
|
484
525
|
const items = new Map(corpus.items.map(item => [item.id, item]));
|
|
@@ -497,21 +538,21 @@ async function closeoutAttempt(pmRoot, repo, options, api, retry, observer, only
|
|
|
497
538
|
const activeChildren = new Map();
|
|
498
539
|
for (const item of items.values()) {
|
|
499
540
|
const parent = item.parent;
|
|
500
|
-
if (typeof parent === "string" && !
|
|
541
|
+
if (typeof parent === "string" && !githubStatusIsTerminal(item.status, workspace.statuses))
|
|
501
542
|
activeChildren.set(parent, (activeChildren.get(parent) ?? 0) + 1);
|
|
502
543
|
}
|
|
503
544
|
const plan = [];
|
|
504
545
|
const checks = [];
|
|
505
546
|
const closures = [];
|
|
506
547
|
const candidates = new Map();
|
|
507
|
-
const unreadable =
|
|
548
|
+
const unreadable = new Map();
|
|
508
549
|
for (const number of [...new Set(numbers)].sort((a, b) => a - b)) {
|
|
509
550
|
let evidence;
|
|
510
551
|
try {
|
|
511
552
|
evidence = await payload(api, `/repos/${repo}/pulls/${number}`);
|
|
512
553
|
}
|
|
513
|
-
catch {
|
|
514
|
-
unreadable.
|
|
554
|
+
catch (error) {
|
|
555
|
+
unreadable.set(number, error instanceof CloseoutReadError ? error.message : "Pull-request evidence unavailable or incomplete.");
|
|
515
556
|
continue;
|
|
516
557
|
}
|
|
517
558
|
const pr = pullRequest(evidence, repo, number);
|
|
@@ -562,7 +603,7 @@ async function closeoutAttempt(pmRoot, repo, options, api, retry, observer, only
|
|
|
562
603
|
code = "explicit_skip";
|
|
563
604
|
reason = "Explicitly excluded by --skip.";
|
|
564
605
|
}
|
|
565
|
-
else if (
|
|
606
|
+
else if (githubStatusIsTerminal(items.get(ref.item).status, workspace.statuses)) {
|
|
566
607
|
code = "already_terminal";
|
|
567
608
|
reason = "Item is already closed or canceled.";
|
|
568
609
|
}
|
|
@@ -570,7 +611,7 @@ async function closeoutAttempt(pmRoot, repo, options, api, retry, observer, only
|
|
|
570
611
|
code = "not_merged";
|
|
571
612
|
reason = "Pull request is confirmed unmerged and cannot deliver this item.";
|
|
572
613
|
}
|
|
573
|
-
else if (items.get(ref.item).type
|
|
614
|
+
else if (isEpic(items.get(ref.item).type)) {
|
|
574
615
|
code = "epic";
|
|
575
616
|
reason = "Epics close when their children are done, not by a pull request.";
|
|
576
617
|
}
|
|
@@ -609,8 +650,8 @@ async function closeoutAttempt(pmRoot, repo, options, api, retry, observer, only
|
|
|
609
650
|
const row = { item: ref.item, pr: number, action: "skip", code, reason };
|
|
610
651
|
plan.push(row);
|
|
611
652
|
if (role !== "context" && pr.merged !== false && items.has(ref.item) && ref.repo.toLowerCase() === repo.toLowerCase()
|
|
612
|
-
&& !skip.has(ref.item) && !
|
|
613
|
-
&& items.get(ref.item).type
|
|
653
|
+
&& !skip.has(ref.item) && !githubStatusIsTerminal(items.get(ref.item).status, workspace.statuses)
|
|
654
|
+
&& !isEpic(items.get(ref.item).type) && !activeChildren.has(ref.item)) {
|
|
614
655
|
const group = candidates.get(ref.item) ?? [];
|
|
615
656
|
group.push({ row, pr, declared: ref.declared, commitsReadable });
|
|
616
657
|
candidates.set(ref.item, group);
|
|
@@ -619,12 +660,12 @@ async function closeoutAttempt(pmRoot, repo, options, api, retry, observer, only
|
|
|
619
660
|
}
|
|
620
661
|
// A failed PR read can hide linkage and a competing claim. Retain its number for
|
|
621
662
|
// every active eligible item; without the PR evidence no item can be proved unrelated.
|
|
622
|
-
for (const number of unreadable) {
|
|
663
|
+
for (const [number, reason] of unreadable) {
|
|
623
664
|
const url = `https://github.com/${repo}/pull/${number}`;
|
|
624
665
|
for (const item of items.values()) {
|
|
625
|
-
if (skip.has(item.id) ||
|
|
666
|
+
if (skip.has(item.id) || githubStatusIsTerminal(item.status, workspace.statuses) || isEpic(item.type) || activeChildren.has(item.id))
|
|
626
667
|
continue;
|
|
627
|
-
const row = { item: item.id, pr: number, action: "skip", code: "head_evidence_unavailable", reason
|
|
668
|
+
const row = { item: item.id, pr: number, action: "skip", code: "head_evidence_unavailable", reason };
|
|
628
669
|
plan.push(row);
|
|
629
670
|
const group = candidates.get(item.id) ?? [];
|
|
630
671
|
group.push({ row, pr: { number, html_url: url, body: null, merged: true, merged_at: null, merge_commit_sha: null, commits: null,
|