pm-github 2026.10.15 → 2026.10.19
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 +25 -0
- package/README.md +295 -28
- package/dist/closeout.d.ts +27 -5
- package/dist/closeout.d.ts.map +1 -1
- package/dist/closeout.js +134 -24
- package/dist/closeout.js.map +1 -1
- package/dist/import-fidelity.d.ts +42 -0
- package/dist/import-fidelity.d.ts.map +1 -0
- package/dist/import-fidelity.js +235 -0
- package/dist/import-fidelity.js.map +1 -0
- package/dist/index.d.ts +145 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +150 -51
- package/dist/index.js.map +1 -1
- package/manifest.json +1 -1
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2026.10.19 - 2026-10-19
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Preserve imported GitHub lifetimes, closure evidence and release attribution while linking cross-provider items ([pm-github-c093](https://github.com/unbraind/pm-github/blob/main/.agents/pm/features/pm-github-c093.toon))
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- Require explicit delivery attribution before merged-PR closeout ([pm-github-uqjf](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-uqjf.toon))
|
|
12
|
+
- The daily release regenerates the changelog with a different status filter than release:check, so an open changelog-unreleased item delivered since the last tag fails the release ([pm-github-l1ot](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-l1ot.toon))
|
|
13
|
+
- Release timeout must report current workflow approval state ([pm-github-b145](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-b145.toon))
|
|
14
|
+
- Preserve legacy GitHub ownership across copied provider examples ([pm-github-b143](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-b143.toon))
|
|
15
|
+
- Reject malformed effective-rule discriminators before closeout ([pm-github-b141](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-b141.toon))
|
|
16
|
+
|
|
17
|
+
## 2026.10.16 - 2026-10-16
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- Add a reusable gated closeout PR workflow ([pm-github-0x3y](https://github.com/unbraind/pm-github/blob/main/.agents/pm/tasks/pm-github-0x3y.toon))
|
|
22
|
+
- Let reusable sync choose its npm project directory ([pm-github-z5z1](https://github.com/unbraind/pm-github/blob/main/.agents/pm/features/pm-github-z5z1.toon))
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- Refuse reusable sync runs outside the caller default branch ([pm-github-q4uz](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-q4uz.toon))
|
|
27
|
+
|
|
3
28
|
## 2026.10.15 - 2026-10-15
|
|
4
29
|
|
|
5
30
|
### Added
|
package/README.md
CHANGED
|
@@ -39,15 +39,17 @@ Only `https://github.com/owner/repo/blob/main/.agents/pm/<folder>/<id>.toon`
|
|
|
39
39
|
links in PR bodies are recognized. Percent-encoded names and nested SDK tracker
|
|
40
40
|
folders are supported; malformed encodings and traversal segments are ignored. Repository comparisons ignore case. Links to
|
|
41
41
|
other repositories, unknown ids, closed/canceled items and `--skip` ids produce
|
|
42
|
-
explicit skip rows. Bare
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
explicit skip rows. Bare mentions and `pm:` trailers are not delivery evidence. Use a whole-line
|
|
43
|
+
`Closes <item-id>` or `pm-github: <item-id>` declaration, a closing keyword before
|
|
44
|
+
the canonical item link, or an item field `github_pr`, `pr_url` or
|
|
45
|
+
`pull_request_url` containing the exact PR URL. Multiple merged PRs explicitly
|
|
46
|
+
claiming one item are reported as `ambiguous_delivery`; none closes it.
|
|
45
47
|
|
|
46
48
|
**A mention is not delivery.** PR bodies routinely link follow-ups, epics and
|
|
47
49
|
related work for context, so a link alone never closes an item. An item is
|
|
48
50
|
closed only when:
|
|
49
51
|
|
|
50
|
-
-
|
|
52
|
+
- its delivery is explicitly declared; a canonical link may be preceded on the same
|
|
51
53
|
line by a GitHub-style closing keyword: `Closes`, `Fixes`, `Resolves` or
|
|
52
54
|
`Delivers` (and their tense forms), for example `- Closes [pm-x](…/pm-x.toon)`;
|
|
53
55
|
- it is not an `Epic`; epics close when their children are done;
|
|
@@ -65,19 +67,34 @@ latest status. Check-run, status and ruleset pages are fully read. Rulesets are
|
|
|
65
67
|
read through GitHub's [effective branch rules API](https://docs.github.com/en/rest/repos/rules#get-rules-for-a-branch).
|
|
66
68
|
The check policy reflects the rules readable at invocation time.
|
|
67
69
|
|
|
68
|
-
When
|
|
69
|
-
|
|
70
|
-
context
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
70
|
+
When either protection source is unreadable (HTTP 403/404), the row is ineligible
|
|
71
|
+
with `required_checks_unverified`. Observed green runs cannot prove that every
|
|
72
|
+
required context ran. Supply `--required-checks $'test (22)\ntest (26)'` (Bash) to declare a
|
|
73
|
+
newline-separated set of required context names when metadata is unavailable.
|
|
74
|
+
Every declared context must conclude **success** on the exact head (or have a
|
|
75
|
+
successful latest legacy status); missing, pending, neutral or skipped contexts
|
|
76
|
+
refuse closure. Readable requirements remain enforced, including app bindings.
|
|
77
|
+
This **declared** policy does not require unrelated optional runs to succeed.
|
|
78
|
+
When both metadata sources are readable, declarations do not replace their policy.
|
|
79
|
+
|
|
80
|
+
With fully readable metadata and no required contexts configured, the report
|
|
81
|
+
labels the **fallback** rule: at least one head check run must exist, every run
|
|
82
|
+
must complete with `success`, `neutral` or `skipped`, and latest legacy statuses
|
|
83
|
+
must succeed. Missing, malformed or incomplete evidence and other metadata errors
|
|
84
|
+
fail closed. The plan reports which rule was used.
|
|
85
|
+
|
|
86
|
+
TOON lists actionable `close`/`refuse` rows individually and summarizes routine skips
|
|
87
|
+
as counts by stable code, for example `skipped: {already_terminal: 30,
|
|
88
|
+
other_repository: 10, duplicate_selection: 5}`. Explicit exclusions use
|
|
89
|
+
`explicit_skip`. Only checks for actionable rows are shown. Use `--full` for every
|
|
90
|
+
plan row and selected PR check. Global `--json` always returns the full shape
|
|
91
|
+
`{command, repo, dry_run, plan, checks, closed, failed}`. Each full plan row is
|
|
92
|
+
`{item, pr, action: "close" | "skip", code, reason}`. Codes distinguish
|
|
93
|
+
`head_evidence_unavailable`, `incomplete_merge`, `head_checks_failed`, and
|
|
94
|
+
`head_checks_passed` without parsing explanations. `checks` reports each PR's rule,
|
|
95
|
+
verdict, explanation and successful check names. `head_checks_passed` remains the success
|
|
96
|
+
code for all passing policies; `rule: "declared"` proves the caller-declared contexts,
|
|
97
|
+
while repository required-check metadata was unavailable. Dry-run rows describe intended
|
|
81
98
|
closes; `closed` counts writes only with `--apply`. `--dry-run` overrides
|
|
82
99
|
`--apply`. All API planning finishes before the first SDK close. Individual SDK
|
|
83
100
|
refusals become skip rows containing a diagnostic code (or a generic failure
|
|
@@ -99,8 +116,11 @@ structured close fields it supports, without overriding tracker policy:
|
|
|
99
116
|
otherwise `Delivered by the linked pull request, merged after its head check gate passed.`
|
|
100
117
|
- `actual_result`: `Merged at <time>; required checks satisfied; successful head checks:
|
|
101
118
|
<names>` when branch protection or rulesets name required checks (the list holds every
|
|
102
|
-
successful head check, optional ones included). Under the
|
|
103
|
-
|
|
119
|
+
successful head check, optional ones included). Under the declared policy it reads
|
|
120
|
+
`Merged at <time>; caller-declared checks concluded success; repository required-check
|
|
121
|
+
metadata was unavailable; successful head checks: <names>`. Under the fallback rule
|
|
122
|
+
(fully readable, empty policy) it reads
|
|
123
|
+
`Merged at <time>; no readable required checks, so every
|
|
104
124
|
completed head check run had to succeed, be neutral or be skipped; successful checks:
|
|
105
125
|
<names>`, or `…; no check reported success` when only neutral or skipped runs exist.
|
|
106
126
|
Only the verified merge time, the applied check policy and sorted successful check
|
|
@@ -111,8 +131,8 @@ criteria remain the only prose source; PR body and comments supply no field text
|
|
|
111
131
|
Completion time is the GitHub merge time. Repeating a completed selection skips
|
|
112
132
|
terminal items and performs zero additional tracker writes. The command edits
|
|
113
133
|
only the tracker; commit it with its history and regenerate the caller's
|
|
114
|
-
changelog in a closeout PR.
|
|
115
|
-
|
|
134
|
+
changelog in a closeout PR. The [automated closeout workflow](#automated-closeout-workflow)
|
|
135
|
+
performs that daily review-PR preparation for fleet repositories.
|
|
116
136
|
|
|
117
137
|
## Revalidate GitHub blockers
|
|
118
138
|
|
|
@@ -444,8 +464,203 @@ Rendered values are also scanned in memory before mutation, so a known finding c
|
|
|
444
464
|
|
|
445
465
|
The `gate` receipt reports the verdict, scanned file count, findings, and allowlisted count. With `--json`, successful reports go to stdout; failures use the host CLI's JSON error envelope on stderr, with the redacted gate report serialized in `detail` (`jq ' .detail | fromjson ' gate-error.json`). Unreadable inputs produce a nonzero JSON refusal.
|
|
446
466
|
|
|
467
|
+
## Self-versioned workflows and Dependabot
|
|
468
|
+
|
|
469
|
+
Both reusable workflows resolve their own **exact** pm-github release version at
|
|
470
|
+
runtime and verify the installed package version. GitHub's documented
|
|
471
|
+
[`job.workflow_sha`](https://docs.github.com/en/actions/reference/workflows-and-actions/contexts#job-context)
|
|
472
|
+
is the commit of the workflow that defines the current job, including a reusable
|
|
473
|
+
workflow. `github.job_workflow_sha` is not a documented context property;
|
|
474
|
+
`job_workflow_sha` is an OIDC claim. The caller checkout and caller commit are
|
|
475
|
+
never used to discover the reusable workflow's version. These workflow identity
|
|
476
|
+
context properties are not available on GitHub Enterprise Server.
|
|
477
|
+
|
|
478
|
+
The resolver uses public `git ls-remote --tags https://github.com/unbraind/pm-github
|
|
479
|
+
'refs/tags/v*'`. It matches lightweight tags directly and annotated tags by their
|
|
480
|
+
peeled `^{}` commit (never the tag object), strips `v`, and requires an exact
|
|
481
|
+
numeric version `N.N.N` or `N.N.N-N`. It reads all matching refs in one request,
|
|
482
|
+
without API pagination or new credentials. Lookup errors and multiple release
|
|
483
|
+
versions on one commit fail closed. Release version discovery needs no GitHub App, and no release-time workflow
|
|
484
|
+
stamping is required. The release workflow keeps its existing token behavior.
|
|
485
|
+
|
|
486
|
+
Pin **release commit SHAs**, with their matching tag in a same-line comment
|
|
487
|
+
(for example `uses: unbraind/pm-github/.github/workflows/pm-github-sync.yml@<40-character-release-sha> # v2026.10.16`).
|
|
488
|
+
New callers omit `pm-github-version`. If no release tag matches the workflow
|
|
489
|
+
commit, the job fails with instructions to pin a release SHA. For compatibility,
|
|
490
|
+
a nonempty deprecated input provides an exact-version fallback with a warning
|
|
491
|
+
only when no tag matches. If a tag matches and the input differs, the job warns
|
|
492
|
+
and uses the tag version. Untrusted input is passed through `env:`, never inserted
|
|
493
|
+
into shell source or warning text. No `latest`, range, or floating install tag is used.
|
|
494
|
+
|
|
495
|
+
GitHub documents [reusable-workflow updates](https://docs.github.com/en/code-security/how-tos/secure-your-supply-chain/secure-your-dependencies/auto-update-actions)
|
|
496
|
+
and [SHA pins and same-line version comments](https://docs.github.com/en/code-security/reference/supply-chain-security/supported-ecosystems-and-repositories#github-actions).
|
|
497
|
+
Dependabot updates SHA-pinned remote `uses:` references and their version comments
|
|
498
|
+
when the pinned commit has a matching release tag. GitHub warns that untagged
|
|
499
|
+
commits can instead advance to the latest commit, which may differ from the latest
|
|
500
|
+
release. A SHA update therefore selects the new runtime-resolved install version without
|
|
501
|
+
editing any caller input.
|
|
502
|
+
|
|
503
|
+
Merge this entry into the caller's `.github/dependabot.yml` (retain other ecosystems
|
|
504
|
+
and disjoint groups such as `codeql-action`):
|
|
505
|
+
|
|
506
|
+
```yaml
|
|
507
|
+
version: 2
|
|
508
|
+
updates:
|
|
509
|
+
- package-ecosystem: "github-actions"
|
|
510
|
+
directory: "/"
|
|
511
|
+
schedule:
|
|
512
|
+
interval: "daily"
|
|
513
|
+
open-pull-requests-limit: 5
|
|
514
|
+
groups:
|
|
515
|
+
pm-toolchain:
|
|
516
|
+
patterns:
|
|
517
|
+
- "unbraind/pm-github"
|
|
518
|
+
# No update-types filter: calendar-version year changes must also update.
|
|
519
|
+
commit-message:
|
|
520
|
+
prefix: "chore"
|
|
521
|
+
include: "scope"
|
|
522
|
+
labels:
|
|
523
|
+
- "dependencies"
|
|
524
|
+
- "github_actions"
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
The group pattern is the repository name, covering both reusable workflow paths.
|
|
528
|
+
Use exactly `pm-toolchain`: the fleet's existing
|
|
529
|
+
[dependabot-auto-merge.yml](.github/workflows/dependabot-auto-merge.yml) enables
|
|
530
|
+
`gh pr merge --auto --squash` for Dependabot PRs in that group, including calendar
|
|
531
|
+
version major changes. The workflow checks the Dependabot author and repository
|
|
532
|
+
owner, then the metadata group or minor/patch update type. Commit prefix and
|
|
533
|
+
labels do not control merge eligibility; the explicit values above keep PRs
|
|
534
|
+
consistent. Ensure the two labels exist (GitHub ignores unknown custom labels).
|
|
535
|
+
Repository auto-merge must be enabled and required branch-protection checks must
|
|
536
|
+
pass; this configuration does not bypass them. It uses the existing workflow's
|
|
537
|
+
`contents: write` and `pull-requests: write` token permissions. See GitHub's
|
|
538
|
+
[Dependabot option definitions](https://docs.github.com/en/code-security/reference/supply-chain-security/dependabot-options-reference)
|
|
539
|
+
for schedules, groups, commit messages and labels.
|
|
540
|
+
|
|
541
|
+
## Automated closeout workflow
|
|
542
|
+
|
|
543
|
+
[`.github/workflows/pm-github-closeout.yml`](.github/workflows/pm-github-closeout.yml)
|
|
544
|
+
is a reusable `workflow_call` that opens or updates **one** PR titled
|
|
545
|
+
`chore(pm): close items delivered by merged pull requests` from `pm-github/closeout`.
|
|
546
|
+
Copy [the caller template](docs/closeout-workflow-caller.yml) into the caller's
|
|
547
|
+
workflows directory and replace `RELEASE_COMMIT_SHA` with a reviewed release
|
|
548
|
+
commit and `vRELEASE_VERSION` with its matching release tag containing
|
|
549
|
+
`github closeout`. The template schedules a daily run and allows manual dispatch.
|
|
550
|
+
Adopting callers is a separate change.
|
|
551
|
+
|
|
552
|
+
The workflow resolves the caller's default branch through the repository API,
|
|
553
|
+
with event metadata as fallback, and refuses other run refs before checkout.
|
|
554
|
+
This works for scheduled payloads without a `repository` object and for branches
|
|
555
|
+
such as `trunk` or `release/stable`. The caller's pinned PM CLI comes from
|
|
556
|
+
`npm ci --ignore-scripts` in `npm-project-dir` (default `.`), which must resolve
|
|
557
|
+
inside the workspace and contain `package.json`. The SDK resolves the tracker
|
|
558
|
+
from the repository root. The exact release version resolved from the workflow commit is installed and verified.
|
|
559
|
+
|
|
560
|
+
`required-checks` is an optional workflow input with an empty default. Set it to
|
|
561
|
+
all required context names, **one context name per line**, when the token cannot read
|
|
562
|
+
branch-protection or ruleset metadata, as is common with `GITHUB_TOKEN`:
|
|
563
|
+
|
|
564
|
+
```yaml
|
|
565
|
+
with:
|
|
566
|
+
required-checks: |
|
|
567
|
+
test (22)
|
|
568
|
+
test (26)
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
Each line is trimmed and empty lines are ignored. Spaces within names such as
|
|
572
|
+
`CI / build` are preserved; names are never split on spaces. The workflow passes
|
|
573
|
+
the newline-joined list as one quoted command argument for TypeScript to parse.
|
|
574
|
+
Without a declaration, unavailable
|
|
575
|
+
metadata refuses closeout and prevents publishing a branch or PR. Keep this list
|
|
576
|
+
aligned with the caller's required checks; it is a caller-owned policy declaration.
|
|
577
|
+
|
|
578
|
+
Each run rebuilds the bot branch from the current default branch and invokes
|
|
579
|
+
`pm github closeout --since 1970-01-01 --apply --json`. Scanning all merge history
|
|
580
|
+
avoids losing deliveries during schedule outages; it costs more API reads for
|
|
581
|
+
large repositories. Terminal items are skipped and the command retains its
|
|
582
|
+
normal eligibility, child/epic, required-check and strict-close rules. Full
|
|
583
|
+
closeout reports stay in runner scratch storage. The PR body lists
|
|
584
|
+
actionable rows with item, PR number, action and code, plus skip counts by reason.
|
|
585
|
+
This compact summary passes its own privacy scan before publication. The caller's `post-closeout-script` defaults to `changelog:full`,
|
|
586
|
+
run through `npm run --if-present` in the npm project; empty disables it.
|
|
587
|
+
The script must emit the root `CHANGELOG.md` and remain inside the publication scope.
|
|
588
|
+
|
|
589
|
+
| Safety boundary | Closeout and sync parity |
|
|
590
|
+
| --- | --- |
|
|
591
|
+
| Entry and install | Caller default-branch guard, contained npm project, exact package version, SHA-pinned Actions, no persisted checkout credentials |
|
|
592
|
+
| Proposed publication | SDK-resolved tracker plus root changelog; setup, extension, lock and checkpoint files excluded; every changed path checked |
|
|
593
|
+
| Privacy | Same fail-closed full-context staged-diff scan after the caller script and immediately before commit; unreadable input refuses publication |
|
|
594
|
+
| Validation | Strict PM health; explicit item/history scope committed together |
|
|
595
|
+
| Branch and PR | Explicit force-with-lease against the observed bot ref; push only bot branch; owned PR lookup excludes forks |
|
|
596
|
+
| No-op | Zero closes or no tracker diff creates no remote branch or PR; caller-only drift unpublished; stale owned PR closed |
|
|
597
|
+
| CI and landing | Dispatch caller checks after publication and branch refresh; request protected squash auto-merge in App mode; report parked approval-required runs |
|
|
598
|
+
|
|
599
|
+
`ci-workflows` defaults to `ci.yml codeql.yml`, covering the fleet's CI and
|
|
600
|
+
CodeQL workflows without querying branch protection. An explicit list replaces
|
|
601
|
+
that inventory (use `ci.yaml` or `codeql.yaml` when appropriate). Missing files
|
|
602
|
+
emit a notice; existing files without a recognized `workflow_dispatch` trigger
|
|
603
|
+
emit a warning naming the one-line repair. Standard block triggers and compact
|
|
604
|
+
`on: workflow_dispatch` / `on: [pull_request, workflow_dispatch]` forms are
|
|
605
|
+
supported. Each dispatched workflow must emit its required context names on
|
|
606
|
+
that event. Zero dispatchable workflows fails; `""` deliberately disables both
|
|
607
|
+
dispatch and automatic landing. Dispatch errors fail the run.
|
|
608
|
+
|
|
609
|
+
GitHub does not evaluate `workflow_dispatch` job checks for PR branch rules,
|
|
610
|
+
even when those checks pass on the head SHA. For unattended landing, configure
|
|
611
|
+
`automation-app-client-id` and the optional `automation_app_private_key` secret.
|
|
612
|
+
After the local gates, the pinned official App-token action mints a token for
|
|
613
|
+
only the caller repository, with contents/pull-requests/actions write and
|
|
614
|
+
checks/statuses read. Publication and refresh use that token, so native
|
|
615
|
+
`pull_request` workflows run without the GITHUB_TOKEN approval prompt and
|
|
616
|
+
provide eligible required checks. Manual head dispatch remains additional
|
|
617
|
+
verification. Empty credentials retain GITHUB_TOKEN and emit a setup notice;
|
|
618
|
+
partial or failed App credentials stop publication. The private key is passed
|
|
619
|
+
only to the token action, after caller scripts have completed.
|
|
620
|
+
|
|
621
|
+
In App mode, after dispatch the workflow requests `gh pr merge --auto --squash`
|
|
622
|
+
with the expected head SHA. A repository that disallows auto-merge emits a notice.
|
|
623
|
+
Without an App, maintainers approve native PR runs, wait for required checks and
|
|
624
|
+
conversations, and merge with an authorized credential; the workflow does not arm
|
|
625
|
+
auto-merge.
|
|
626
|
+
GitHub enforces required checks and conversation resolution. The same run
|
|
627
|
+
polls once per minute: when the PR is `BEHIND`, it requests a branch update
|
|
628
|
+
against the expected head, then dispatches all selected workflows and, in App
|
|
629
|
+
mode, requests auto-merge again when the new head appears. App mode stops within
|
|
630
|
+
forty minutes, inside its token's one-hour lifetime; legacy mode has a two-hour bound. The
|
|
631
|
+
existing daily schedules and separate sync/closeout concurrency groups remain.
|
|
632
|
+
A base change after the monitor window waits for the next daily rebuild.
|
|
633
|
+
Unresolved conversations continue to block landing.
|
|
634
|
+
|
|
635
|
+
See [the caller audit and one-time setup](docs/bot-pr-automation.md) for the App
|
|
636
|
+
credential mapping, CodeQL trigger changes and explicit CI overrides. GitHub
|
|
637
|
+
documents [the token approval behavior](https://docs.github.com/en/actions/concepts/security/github_token)
|
|
638
|
+
and [why manual job checks do not satisfy PR branch rules](https://docs.github.com/en/pull-requests/how-tos/merge-and-close-pull-requests/troubleshooting-required-status-checks).
|
|
639
|
+
|
|
640
|
+
The caller template uses top-level `permissions: {}` and grants its
|
|
641
|
+
`gated-closeout` job only `contents: write`, `pull-requests: write`, `checks: read`
|
|
642
|
+
and `actions: write`. Closeout uses `checks: read` instead of sync's `issues: read`
|
|
643
|
+
to retrieve head-check evidence ([GitHub Checks API permissions](https://docs.github.com/en/rest/checks/runs#list-check-runs-for-a-git-reference)).
|
|
644
|
+
The reusable workflow cannot elevate the caller's token
|
|
645
|
+
permissions ([GitHub reusable workflow permissions](https://docs.github.com/en/actions/reference/workflows-and-actions/reusing-workflow-configurations)).
|
|
646
|
+
Repository Actions settings must allow Actions to create PRs. Read denial stops
|
|
647
|
+
closeout before push with a diagnostic, including `required_checks_unverified` and
|
|
648
|
+
unavailable head-check evidence
|
|
649
|
+
reported by the command as a skip when it blocks a local, existing, non-terminal,
|
|
650
|
+
non-excluded tracker item. Unlinked PRs and foreign/terminal/excluded links do
|
|
651
|
+
not fail the run for unavailable evidence. Incomplete merge facts use a separate
|
|
652
|
+
`incomplete_merge` refusal before any head lookup. The pinned extension must
|
|
653
|
+
emit plan decision codes; repin to a release containing this contract. A push/PR denial fails publication and
|
|
654
|
+
CI dispatch denial fails the job explicitly. A PR creation denial may leave the
|
|
655
|
+
gated bot branch pushed, and a dispatch denial leaves the review PR open; neither
|
|
656
|
+
case changes the default branch or merges anything. Explicit dispatch is used
|
|
657
|
+
for reliable caller CI invocation with `GITHUB_TOKEN`
|
|
658
|
+
([GitHub token event behavior](https://docs.github.com/en/actions/concepts/security/github_token)).
|
|
659
|
+
|
|
447
660
|
## Automated sync workflow
|
|
448
661
|
|
|
662
|
+
The reusable workflow's first step resolves the caller's default branch using the repository API with the job token, falling back to `github.event.repository.default_branch` only when present. Scheduled payloads need no `repository` object. If neither source provides a valid branch, the job fails closed with a clear error before checkout. It requires `github.ref` to equal `refs/heads/<resolved-default-branch>`. A mismatch fails before checkout or any sync operation with a one-line error naming both refs. Scheduled calls and manual `workflow_dispatch` calls on the default branch are accepted; manual dispatches selecting another branch are refused. Checkout, sync-branch preparation, PR lookup/base and tracker links all use that resolved branch, including defaults such as `trunk` or `release/stable`. Callers need no guard of their own. Repin the reusable workflow to a release commit containing this guard to receive it.
|
|
663
|
+
|
|
449
664
|
Fleet repos sync their issues into pm through ONE audited implementation: the reusable `workflow_call` workflow at [`.github/workflows/pm-github-sync.yml`](.github/workflows/pm-github-sync.yml). Its safety order is validate → dry-run plan → **gated import** → verify the repeat import is a no-op → caller post-import script → **full changed-path scope check and privacy gate** → strict health → repeat scope check and scan immediately before commit/push/review PR → caller CI dispatch. The default `post-import-script` is `changelog:full`, invoked with `npm run --if-present`: it runs the caller's own generator when present, so imported closed issues and their changelog entries land in the same commit. Actions are pinned by commit SHA. The job requests `contents: write`, `pull-requests: write`, `issues: read`, and `actions: write` for dispatch.
|
|
450
665
|
|
|
451
666
|
When the tracker has no changes, regenerated `CHANGELOG.md` drift is discarded and the run emits a notice that the caller's own `changelog:check` owns that drift. The sync closes any owned open review PR and exits successfully without pushing or dispatching CI. Changelog changes are published only together with tracker changes.
|
|
@@ -461,10 +676,12 @@ Optional inputs:
|
|
|
461
676
|
|
|
462
677
|
| Input | Default | Behavior |
|
|
463
678
|
| --- | --- | --- |
|
|
679
|
+
| `npm-project-dir` | `.` | Relative npm project directory for `npm ci --ignore-scripts`, the CLI `node_modules/.bin` PATH entry, and `post-import-script`. Must have `package.json`, contain no `..` segments, and resolve inside the workspace (including symlinks); invalid paths fail before caching/install. The tracker and publication scope stay at the repository root. |
|
|
464
680
|
| `post-import-script` | `changelog:full` | Caller npm script name invoked with `npm run --if-present` after import and before the final scan. Override for a different package script, or set `""` to disable. Missing scripts are skipped. Raw shell commands are not accepted. Generated `CHANGELOG.md` is staged with the tracker. Changing any file outside the documented scope fails the run, whether staged, unstaged, or untracked. |
|
|
465
|
-
| `
|
|
681
|
+
| `automation-app-client-id` | `""` | Optional GitHub App client ID, paired with the `automation_app_private_key` workflow-call secret. Enables eligible native PR runs through a fresh repo-scoped token; absent credentials retain GITHUB_TOKEN with a notice. |
|
|
682
|
+
| `ci-workflows` | `ci.yml codeql.yml` | Explicit whitespace-separated filename override in the caller's `.github/workflows`. Missing/non-dispatchable workflows are reported; zero dispatchable workflows fails. Empty disables dispatch and auto-merge. See the CI and landing contract above. |
|
|
466
683
|
|
|
467
|
-
A fleet repo owns the schedule and calls it with
|
|
684
|
+
A fleet repo owns the schedule and calls it with a pinned release SHA (see [`docs/sync-workflow-caller.yml`](docs/sync-workflow-caller.yml)):
|
|
468
685
|
|
|
469
686
|
```yaml
|
|
470
687
|
name: Sync GitHub issues into pm
|
|
@@ -481,14 +698,31 @@ permissions:
|
|
|
481
698
|
|
|
482
699
|
jobs:
|
|
483
700
|
gated-sync:
|
|
484
|
-
uses: unbraind/pm-github/.github/workflows/pm-github-sync.yml@RELEASE_COMMIT_SHA
|
|
701
|
+
uses: unbraind/pm-github/.github/workflows/pm-github-sync.yml@RELEASE_COMMIT_SHA # vRELEASE_VERSION
|
|
485
702
|
with:
|
|
486
|
-
|
|
703
|
+
automation-app-client-id: ${{ vars.PM_GITHUB_APP_CLIENT_ID }}
|
|
704
|
+
secrets:
|
|
705
|
+
automation_app_private_key: ${{ secrets.PM_GITHUB_APP_PRIVATE_KEY }}
|
|
487
706
|
```
|
|
488
707
|
|
|
489
|
-
Replace
|
|
490
|
-
|
|
491
|
-
|
|
708
|
+
Replace `RELEASE_COMMIT_SHA` with a reviewed release commit containing this change and `vRELEASE_VERSION` with its matching tag. Configure [Dependabot](#self-versioned-workflows-and-dependabot) to maintain future pins automatically. The selected npm project needs a committed npm lockfile (`package-lock.json` or `npm-shrinkwrap.json`) and `@unbrained/pm-cli` as a devDependency (the workflow's `npm ci` provides the CLI) and its pm tracker at `.agents/pm` committed on the caller's default branch. Under **Settings → Actions → General → Workflow permissions** it must allow read and write token permissions and enable **Allow GitHub Actions to create and approve pull requests** (an organization can disable this), or the run pushes the sync branch and then fails to open the review PR. The caller must grant `actions: write`; a reusable workflow cannot elevate its caller's permissions.
|
|
709
|
+
|
|
710
|
+
For a caller such as `unbraind/pm-rust` with no root `package.json`, add
|
|
711
|
+
`npm-project-dir: tests/oracle` under `with:`. Its `tests/oracle/package.json`
|
|
712
|
+
provides the CLI and post-import npm script; Git and pm tracker operations still
|
|
713
|
+
run from the repository root. A script that regenerates the changelog must write
|
|
714
|
+
the repository-root `CHANGELOG.md` to stay within the publication allowlist.
|
|
715
|
+
|
|
716
|
+
Bot-created PRs using GITHUB_TOKEN may have `pull_request` runs parked in
|
|
717
|
+
`action_required`. The optional App token makes publication and branch refresh
|
|
718
|
+
start native eligible PR workflows automatically. Configured manual workflows
|
|
719
|
+
are also dispatched on the bot head; these jobs are additional verification,
|
|
720
|
+
because GitHub excludes manual job checks from PR branch-rule evaluation. Each
|
|
721
|
+
manual workflow must support `workflow_dispatch` and produce its intended check
|
|
722
|
+
names on that event. Parked-run notices include their URLs. A no-change sync
|
|
723
|
+
closes its stale owned review PR and dispatches nothing. See GitHub's
|
|
724
|
+
[token event rules](https://docs.github.com/en/actions/concepts/security/github_token)
|
|
725
|
+
and [required-check eligibility](https://docs.github.com/en/pull-requests/how-tos/merge-and-close-pull-requests/troubleshooting-required-status-checks).
|
|
492
726
|
|
|
493
727
|
### Installed CLI timeout acceptance
|
|
494
728
|
|
|
@@ -547,3 +781,36 @@ itself lists each affected stream in its output; `pm history --verify <id>` spot
|
|
|
547
781
|
content, so `reconcile` only re-greens the hash chain (no data loss) — see the authoritative
|
|
548
782
|
[pm-cli merge-safety guide](https://github.com/unbraind/pm-cli/blob/main/docs/MERGE_SAFETY.md). The
|
|
549
783
|
older blunt `pm history-repair --all` remains available as a lower-level primitive.
|
|
784
|
+
|
|
785
|
+
## What an imported issue looks like
|
|
786
|
+
|
|
787
|
+
An issue owned by the GitHub importer keeps GitHub's creation and closure dates in the native lifecycle fields used by momentum and cycle-time readers. The import records a new, append-only before/after history event at the time of synchronization; it does not rewrite old events. The public SDK's high-level create/update commands reserve these dates, so finalization uses its public item lock, serialization and history primitives.
|
|
788
|
+
|
|
789
|
+
```yaml
|
|
790
|
+
status: closed
|
|
791
|
+
created_at: "2026-09-02T10:00:00Z"
|
|
792
|
+
updated_at: "2026-10-16T12:00:00Z"
|
|
793
|
+
closed_at: "2026-09-04T12:00:00Z"
|
|
794
|
+
completed_at: "2026-09-04T12:00:00Z"
|
|
795
|
+
close_reason: 'Closed on GitHub as completed on 2026-09-04T12:00:00Z; closing PR https://github.com/example/project/pull/8 ("Restore the parser")'
|
|
796
|
+
release: v2026.9.5
|
|
797
|
+
github_url: https://github.com/example/project/issues/7
|
|
798
|
+
github_number: 7
|
|
799
|
+
github_state: closed
|
|
800
|
+
github_state_reason: completed
|
|
801
|
+
github_created_at: "2026-09-02T10:00:00Z"
|
|
802
|
+
github_updated_at: "2026-09-04T12:00:00Z"
|
|
803
|
+
github_closed_at: "2026-09-04T12:00:00Z"
|
|
804
|
+
github_release: v2026.9.5
|
|
805
|
+
github_link_only: false
|
|
806
|
+
```
|
|
807
|
+
|
|
808
|
+
`completed` maps to `closed`; `not_planned` and `duplicate` map to `canceled`. An open or reopened issue maps to `open` and clears old closure evidence and import-assigned release attribution. A closure reason names the closing commit from the REST timeline or closing PR/commit identified by GraphQL `ClosedEvent`, when available. A `ConnectedEvent` supplies a quoted linked PR without claiming that the association caused the closure. Otherwise it says `Closed on GitHub as <reason> on <date>`. Closing a report alone does not establish that its described defect or privacy exposure was repaired.
|
|
809
|
+
|
|
810
|
+
Release attribution uses the actual dates of locally available calendar tags, including annotated tag dates. A closed issue belongs to the first tag at or after its closure: before the first tag goes to that first release, between two tags goes to the second, and after the latest tag remains pending. Fetch the complete tag history before importing; the reusable sync workflow already does this. Regenerate with pm-changelog's `--respect-item-release` so a historical import stays in its declared release window.
|
|
811
|
+
|
|
812
|
+
Before creating an item, import checks the complete tracker for its provenance tag, GitHub URLs in provenance or link metadata, and Linear/Jira/ADO links referenced by the GitHub issue body. A unique existing external owner receives the GitHub tag and source metadata; its title, body, provider identity, creation/completion dates and release remain intact. A persisted `github_link_only` marker preserves that ownership on subsequent syncs. Free-text descriptions and bodies do not declare GitHub ownership. Ambiguous matches refuse the import before writes with `--gate` or `--atomic`. A plain multi-issue import may write earlier issues before rejecting a later ambiguous match.
|
|
813
|
+
|
|
814
|
+
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.
|
|
815
|
+
|
|
816
|
+
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.
|
package/dist/closeout.d.ts
CHANGED
|
@@ -13,11 +13,15 @@ export interface CloseoutRow {
|
|
|
13
13
|
pr: number;
|
|
14
14
|
action: "close" | "skip";
|
|
15
15
|
reason: string;
|
|
16
|
+
code: CloseoutCode;
|
|
16
17
|
}
|
|
18
|
+
/** Stable decisions used by automation and compact skip counts. */
|
|
19
|
+
export type CloseoutCode = "already_terminal" | "other_repository" | "duplicate_selection" | "explicit_skip" | "unknown_item" | "epic" | "active_children" | "not_delivery" | "close_failed" | "ambiguous_delivery" | "head_checks_passed" | "head_checks_failed" | "head_evidence_unavailable" | "required_checks_unverified" | "incomplete_merge";
|
|
17
20
|
/** Which head-check policy was evaluated for one selected pull request. */
|
|
18
21
|
interface CheckGate {
|
|
19
22
|
pr: number;
|
|
20
|
-
|
|
23
|
+
code: CloseoutCode;
|
|
24
|
+
rule: "required" | "declared" | "fallback" | "unavailable";
|
|
21
25
|
reason: string;
|
|
22
26
|
passed: string[];
|
|
23
27
|
ok: boolean;
|
|
@@ -40,8 +44,8 @@ interface PullRequest {
|
|
|
40
44
|
};
|
|
41
45
|
};
|
|
42
46
|
}
|
|
43
|
-
/** Verify
|
|
44
|
-
export declare function checkCloseoutHead(api: CloseoutApi, repo: string, pr: PullRequest): Promise<CheckGate>;
|
|
47
|
+
/** Verify metadata-backed or caller-declared requirements before considering observed head runs. */
|
|
48
|
+
export declare function checkCloseoutHead(api: CloseoutApi, repo: string, pr: PullRequest, declared?: string[]): Promise<CheckGate>;
|
|
45
49
|
/**
|
|
46
50
|
* Whether a closing keyword directly precedes a link, given the line text before the link.
|
|
47
51
|
*
|
|
@@ -64,8 +68,9 @@ export declare class CloseoutUsageError extends Error {
|
|
|
64
68
|
/**
|
|
65
69
|
* Describe the verified check evidence for a passed gate without overstating it.
|
|
66
70
|
*
|
|
67
|
-
* The required rule
|
|
68
|
-
*
|
|
71
|
+
* The required rule reports required-check success; the declared rule reports caller-declared success
|
|
72
|
+
* and unavailable repository metadata. Both list every successful head check (optional ones included).
|
|
73
|
+
* The fallback rule (fully readable metadata with no required contexts)
|
|
69
74
|
* may pass on neutral or skipped runs alone, so it states that policy and lists successful checks only when any exist.
|
|
70
75
|
*/
|
|
71
76
|
export declare function checkEvidence(gate: CheckGate): string;
|
|
@@ -79,5 +84,22 @@ export declare function runCloseout(pmRoot: string, repo: string | undefined, op
|
|
|
79
84
|
closed: number;
|
|
80
85
|
failed: number;
|
|
81
86
|
}>;
|
|
87
|
+
/** Keep actionable decisions and their checks while counting routine skips by stable code. */
|
|
88
|
+
export declare function compactCloseoutReport(report: Awaited<ReturnType<typeof runCloseout>>): {
|
|
89
|
+
command: string;
|
|
90
|
+
repo: string;
|
|
91
|
+
dry_run: boolean;
|
|
92
|
+
closed: number;
|
|
93
|
+
failed: number;
|
|
94
|
+
plan: {
|
|
95
|
+
item: string;
|
|
96
|
+
pr: number;
|
|
97
|
+
reason: string;
|
|
98
|
+
code: CloseoutCode;
|
|
99
|
+
action: string;
|
|
100
|
+
}[];
|
|
101
|
+
skipped: Partial<Record<CloseoutCode, number>>;
|
|
102
|
+
checks: CheckGate[];
|
|
103
|
+
};
|
|
82
104
|
export {};
|
|
83
105
|
//# sourceMappingURL=closeout.d.ts.map
|
package/dist/closeout.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"closeout.d.ts","sourceRoot":"","sources":["../closeout.ts"],"names":[],"mappings":"AAGA,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,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"closeout.d.ts","sourceRoot":"","sources":["../closeout.ts"],"names":[],"mappings":"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"}
|