paperclip-github-plugin 0.8.0 → 0.8.1
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/README.md +5 -5
- package/dist/manifest.js +1 -1
- package/dist/worker.js +41 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -62,7 +62,7 @@ If a company already has a Paperclip project bound to a GitHub repository worksp
|
|
|
62
62
|
|
|
63
63
|
### Status sync with delivery context
|
|
64
64
|
|
|
65
|
-
The plugin does more than mirror issue text. It looks at linked pull requests, mergeability, CI, review threads, and trusted new GitHub comments so imported Paperclip issues can reflect where the work actually is. When GitHub links an issue to a pull request in another repository, GitHub Sync now follows that pull request's actual repository for status checks, review
|
|
65
|
+
The plugin does more than mirror issue text. It looks at linked pull requests, mergeability, CI, review decisions, review threads, and trusted new GitHub comments so imported Paperclip issues can reflect where the work actually is. When GitHub links an issue to a pull request in another repository, GitHub Sync now follows that pull request's actual repository for status checks, review state, and deep links instead of assuming the issue repository. When sync closes an imported issue as `done` or `cancelled`, it also clears any pending Paperclip review or approval execution state so the host accepts the terminal transition cleanly.
|
|
66
66
|
|
|
67
67
|
### Company KPI dashboard
|
|
68
68
|
|
|
@@ -78,7 +78,7 @@ Each mapped project can expose a **Pull Requests** entry in the sidebar that ope
|
|
|
78
78
|
|
|
79
79
|
Paperclip issue linkage on the queue prefers the GitHub issue that the pull request closes, so imported GitHub issues and delivery work stay connected in the same project view. If a pull request has no closing-issue-backed link yet, the queue falls back to the Paperclip issue created directly from that pull request and updates the table immediately when that create action returns.
|
|
80
80
|
|
|
81
|
-
Those pull-request-created Paperclip issues also stay in the scheduled/manual sync loop even when the pull request does not close a GitHub issue. GitHub Sync checks their CI, merge state, and review threads so new failures or
|
|
81
|
+
Those pull-request-created Paperclip issues also stay in the scheduled/manual sync loop even when the pull request does not close a GitHub issue. GitHub Sync checks their CI, merge state, review decision, and review threads so new failures or requested feedback move the Paperclip issue back into active work. The same durable PR link is written when an agent creates a PR through the plugin tool with `paperclipIssueId`, when an authenticated agent records a `gh`-created PR through the agent API route with `paperclipIssueId`, or when an operator manually links an unlinked issue from the issue page.
|
|
82
82
|
|
|
83
83
|
The issue detail panel and sync-created comment annotations also preserve cross-repository linked pull requests, showing those PRs with their real repository path so operators land in the right place on GitHub.
|
|
84
84
|
|
|
@@ -155,8 +155,8 @@ When the local Paperclip API is available, the plugin also syncs labels by name,
|
|
|
155
155
|
| Open issue with no linked pull request, created by a repository maintainer | `todo` on first import |
|
|
156
156
|
| Open issue with no linked pull request | Configured default status, which defaults to `backlog` |
|
|
157
157
|
| Open issue with a linked pull request and unfinished CI | `in_progress` |
|
|
158
|
-
| Open issue with failing CI, a non-mergeable linked pull request, or unresolved review threads | `todo`, or `in_progress` when GitHub Sync can hand the work back to an executor |
|
|
159
|
-
| Open issue with green CI, a merge-ready linked pull request, and all review threads resolved | `in_review` |
|
|
158
|
+
| Open issue with failing CI, a non-mergeable linked pull request, requested changes, or unresolved review threads | `todo`, or `in_progress` when GitHub Sync can hand the work back to an executor |
|
|
159
|
+
| Open issue with green CI, a merge-ready linked pull request, no requested changes, and all review threads resolved | `in_review` |
|
|
160
160
|
| Closed issue completed as finished work | `done` |
|
|
161
161
|
| Closed issue closed as `not_planned` or `duplicate` | `cancelled` |
|
|
162
162
|
|
|
@@ -165,7 +165,7 @@ Additional behavior:
|
|
|
165
165
|
- Open issues with no linked pull request that are created by a verified repository maintainer/admin bypass the default imported status and start in `todo`.
|
|
166
166
|
- When Paperclip board access is connected for a company, the advanced assignee dropdowns list both company agents and `Me` for the connected board user.
|
|
167
167
|
- Newly imported issues that finish sync in `todo` and are assigned to an agent enqueue an assignee wakeup so the agent can pick them up promptly.
|
|
168
|
-
- For linked pull requests, GitHub Sync treats merge-conflict, behind-branch, blocked, draft,
|
|
168
|
+
- For linked pull requests, GitHub Sync treats merge-conflict, behind-branch, blocked, draft, unstable merge states, and requested-changes review decisions as executor work, while merge-ready states such as `CLEAN` and `HAS_HOOKS` can move work into `in_review` when CI is green and there are no requested changes or unresolved review threads.
|
|
169
169
|
- When sync moves work into `in_review`, GitHub Sync first follows the Paperclip issue execution policy's current reviewer or approver when that stage is visible on the issue. If Paperclip exposes an internal review or approval stage but not yet the participant, the plugin falls back to the configured reviewer or approver handoff assignee. If the transition is only a healthy linked-PR wait with no visible internal review or approval stage, GitHub Sync leaves the issue unassigned so it can wait on normal maintainer review without waking an internal owner.
|
|
170
170
|
- When sync moves work back into active execution, GitHub Sync first follows the Paperclip issue execution policy `returnAssignee` when it is available. Otherwise it falls back to the configured executor handoff assignee and then to the default imported assignee.
|
|
171
171
|
- Sync-driven handoffs to agent assignees best-effort enqueue an explicit wakeup so the next reviewer, approver, or executor can pick the issue up even when their agent is not running heartbeats.
|
package/dist/manifest.js
CHANGED
|
@@ -535,7 +535,7 @@ var COMPANY_METRIC_API_ROUTE_URL_PATH = `/api/plugins/${GITHUB_SYNC_PLUGIN_ID}/a
|
|
|
535
535
|
var require2 = createRequire(import.meta.url);
|
|
536
536
|
var packageJson = require2("../package.json");
|
|
537
537
|
var SCHEDULE_TICK_CRON = "* * * * *";
|
|
538
|
-
var MANIFEST_VERSION = "0.8.
|
|
538
|
+
var MANIFEST_VERSION = "0.8.1"?.trim() || typeof packageJson.version === "string" && packageJson.version.trim() || process.env.npm_package_version?.trim() || "0.0.0-dev";
|
|
539
539
|
var manifest = {
|
|
540
540
|
id: GITHUB_SYNC_PLUGIN_ID,
|
|
541
541
|
apiVersion: 1,
|
package/dist/worker.js
CHANGED
|
@@ -812,6 +812,7 @@ var GITHUB_PULL_REQUEST_CI_CONTEXTS_QUERY = `
|
|
|
812
812
|
pullRequest(number: $pullRequestNumber) {
|
|
813
813
|
mergeable
|
|
814
814
|
mergeStateStatus
|
|
815
|
+
reviewDecision
|
|
815
816
|
statusCheckRollup {
|
|
816
817
|
contexts(first: 100, after: $after) {
|
|
817
818
|
pageInfo {
|
|
@@ -877,6 +878,7 @@ var GITHUB_REPOSITORY_OPEN_PULL_REQUEST_STATUSES_QUERY = `
|
|
|
877
878
|
number
|
|
878
879
|
mergeable
|
|
879
880
|
mergeStateStatus
|
|
881
|
+
reviewDecision
|
|
880
882
|
reviewThreads(first: 100) {
|
|
881
883
|
pageInfo {
|
|
882
884
|
hasNextPage
|
|
@@ -917,6 +919,7 @@ var GITHUB_PROJECT_PULL_REQUEST_BASE_FIELDS = `
|
|
|
917
919
|
state
|
|
918
920
|
mergeable
|
|
919
921
|
mergeStateStatus
|
|
922
|
+
reviewDecision
|
|
920
923
|
createdAt
|
|
921
924
|
updatedAt
|
|
922
925
|
baseRefName
|
|
@@ -1003,6 +1006,7 @@ var GITHUB_PROJECT_PULL_REQUEST_METRICS_FIELDS = `
|
|
|
1003
1006
|
number
|
|
1004
1007
|
mergeable
|
|
1005
1008
|
mergeStateStatus
|
|
1009
|
+
reviewDecision
|
|
1006
1010
|
baseRefName
|
|
1007
1011
|
reviews(first: 100) {
|
|
1008
1012
|
pageInfo {
|
|
@@ -5535,11 +5539,23 @@ function normalizeGitHubPullRequestMergeStateStatus(value) {
|
|
|
5535
5539
|
return "unknown";
|
|
5536
5540
|
}
|
|
5537
5541
|
}
|
|
5542
|
+
function normalizeGitHubPullRequestReviewDecision(value) {
|
|
5543
|
+
switch (typeof value === "string" ? value.trim().toLowerCase() : "") {
|
|
5544
|
+
case "approved":
|
|
5545
|
+
return "approved";
|
|
5546
|
+
case "changes_requested":
|
|
5547
|
+
return "changes_requested";
|
|
5548
|
+
case "review_required":
|
|
5549
|
+
return "review_required";
|
|
5550
|
+
default:
|
|
5551
|
+
return "unknown";
|
|
5552
|
+
}
|
|
5553
|
+
}
|
|
5538
5554
|
function isGitHubPullRequestActionRequiredForSync(pullRequest) {
|
|
5539
|
-
return pullRequest.mergeability === "conflicting" || ACTION_REQUIRED_GITHUB_PULL_REQUEST_MERGE_STATE_STATUSES.has(pullRequest.mergeStateStatus);
|
|
5555
|
+
return pullRequest.reviewDecision === "changes_requested" || pullRequest.mergeability === "conflicting" || ACTION_REQUIRED_GITHUB_PULL_REQUEST_MERGE_STATE_STATUSES.has(pullRequest.mergeStateStatus);
|
|
5540
5556
|
}
|
|
5541
5557
|
function isGitHubPullRequestReviewReadyForSync(pullRequest) {
|
|
5542
|
-
if (pullRequest.ciState !== "green" || pullRequest.hasUnresolvedReviewThreads) {
|
|
5558
|
+
if (pullRequest.ciState !== "green" || pullRequest.hasUnresolvedReviewThreads || pullRequest.reviewDecision === "changes_requested") {
|
|
5543
5559
|
return false;
|
|
5544
5560
|
}
|
|
5545
5561
|
return REVIEW_READY_GITHUB_PULL_REQUEST_MERGE_STATE_STATUSES.has(pullRequest.mergeStateStatus);
|
|
@@ -5572,6 +5588,9 @@ function listGitHubPullRequestSyncBlockingConditions(pullRequest) {
|
|
|
5572
5588
|
if (pullRequest.hasUnresolvedReviewThreads) {
|
|
5573
5589
|
conditions.push("unresolved review threads");
|
|
5574
5590
|
}
|
|
5591
|
+
if (pullRequest.reviewDecision === "changes_requested") {
|
|
5592
|
+
conditions.push("requested changes");
|
|
5593
|
+
}
|
|
5575
5594
|
return conditions;
|
|
5576
5595
|
}
|
|
5577
5596
|
function tryBuildGitHubPullRequestStatusSnapshotFromBatchNode(node, repository) {
|
|
@@ -5589,7 +5608,8 @@ function tryBuildGitHubPullRequestStatusSnapshotFromBatchNode(node, repository)
|
|
|
5589
5608
|
hasUnresolvedReviewThreads: reviewThreadSummary.unresolvedReviewThreads > 0,
|
|
5590
5609
|
ciState,
|
|
5591
5610
|
mergeability: normalizeGitHubPullRequestMergeability(node.mergeable),
|
|
5592
|
-
mergeStateStatus: normalizeGitHubPullRequestMergeStateStatus(node.mergeStateStatus)
|
|
5611
|
+
mergeStateStatus: normalizeGitHubPullRequestMergeStateStatus(node.mergeStateStatus),
|
|
5612
|
+
reviewDecision: normalizeGitHubPullRequestReviewDecision(node.reviewDecision)
|
|
5593
5613
|
};
|
|
5594
5614
|
}
|
|
5595
5615
|
function tryBuildGitHubPullRequestCiStateFromBatchNode(node) {
|
|
@@ -5646,6 +5666,7 @@ async function getGitHubPullRequestCiSnapshot(octokit, repository, pullRequestNu
|
|
|
5646
5666
|
const contexts = [];
|
|
5647
5667
|
let mergeability = "unknown";
|
|
5648
5668
|
let mergeStateStatus = "unknown";
|
|
5669
|
+
let reviewDecision = "unknown";
|
|
5649
5670
|
let after;
|
|
5650
5671
|
do {
|
|
5651
5672
|
const response = await octokit.graphql(GITHUB_PULL_REQUEST_CI_CONTEXTS_QUERY, {
|
|
@@ -5656,6 +5677,7 @@ async function getGitHubPullRequestCiSnapshot(octokit, repository, pullRequestNu
|
|
|
5656
5677
|
});
|
|
5657
5678
|
mergeability = normalizeGitHubPullRequestMergeability(response.repository?.pullRequest?.mergeable);
|
|
5658
5679
|
mergeStateStatus = normalizeGitHubPullRequestMergeStateStatus(response.repository?.pullRequest?.mergeStateStatus);
|
|
5680
|
+
reviewDecision = normalizeGitHubPullRequestReviewDecision(response.repository?.pullRequest?.reviewDecision);
|
|
5659
5681
|
const connection = response.repository?.pullRequest?.statusCheckRollup?.contexts;
|
|
5660
5682
|
const nodes = connection?.nodes ?? [];
|
|
5661
5683
|
for (const node of nodes) {
|
|
@@ -5682,7 +5704,8 @@ async function getGitHubPullRequestCiSnapshot(octokit, repository, pullRequestNu
|
|
|
5682
5704
|
return {
|
|
5683
5705
|
ciState: classifyGitHubPullRequestCiState(contexts),
|
|
5684
5706
|
mergeability,
|
|
5685
|
-
mergeStateStatus
|
|
5707
|
+
mergeStateStatus,
|
|
5708
|
+
reviewDecision
|
|
5686
5709
|
};
|
|
5687
5710
|
}
|
|
5688
5711
|
async function getGitHubPullRequestStatusSnapshot(octokit, repository, pullRequestNumber, pullRequestStatusCache, options) {
|
|
@@ -5696,14 +5719,15 @@ async function getGitHubPullRequestStatusSnapshot(octokit, repository, pullReque
|
|
|
5696
5719
|
setCachedGitHubPullRequestStatusSnapshot(pullRequestStatusCache, cachedSnapshot);
|
|
5697
5720
|
return cachedSnapshot;
|
|
5698
5721
|
}
|
|
5699
|
-
if (options?.reviewThreadSummary && options.ciState && options.mergeability !== void 0 && options.mergeStateStatus !== void 0) {
|
|
5722
|
+
if (options?.reviewThreadSummary && options.ciState && options.mergeability !== void 0 && options.mergeStateStatus !== void 0 && options.reviewDecision !== void 0) {
|
|
5700
5723
|
const snapshot = cacheGitHubPullRequestStatusSnapshot(repository, {
|
|
5701
5724
|
number: pullRequestNumber,
|
|
5702
5725
|
repositoryUrl: repository.url,
|
|
5703
5726
|
hasUnresolvedReviewThreads: options.reviewThreadSummary.unresolvedReviewThreads > 0,
|
|
5704
5727
|
ciState: options.ciState,
|
|
5705
5728
|
mergeability: options.mergeability,
|
|
5706
|
-
mergeStateStatus: options.mergeStateStatus
|
|
5729
|
+
mergeStateStatus: options.mergeStateStatus,
|
|
5730
|
+
reviewDecision: options.reviewDecision
|
|
5707
5731
|
});
|
|
5708
5732
|
setCachedGitHubPullRequestStatusSnapshot(pullRequestStatusCache, snapshot);
|
|
5709
5733
|
return snapshot;
|
|
@@ -5717,10 +5741,11 @@ async function getGitHubPullRequestStatusSnapshot(octokit, repository, pullReque
|
|
|
5717
5741
|
const loadSnapshotPromise = (async () => {
|
|
5718
5742
|
const [reviewThreadSummary, ciSnapshot] = await Promise.all([
|
|
5719
5743
|
options?.reviewThreadSummary ?? getOrLoadCachedGitHubPullRequestReviewThreadSummary(octokit, repository, pullRequestNumber),
|
|
5720
|
-
options?.ciState && options.mergeability !== void 0 && options.mergeStateStatus !== void 0 ? {
|
|
5744
|
+
options?.ciState && options.mergeability !== void 0 && options.mergeStateStatus !== void 0 && options.reviewDecision !== void 0 ? {
|
|
5721
5745
|
ciState: options.ciState,
|
|
5722
5746
|
mergeability: options.mergeability,
|
|
5723
|
-
mergeStateStatus: options.mergeStateStatus
|
|
5747
|
+
mergeStateStatus: options.mergeStateStatus,
|
|
5748
|
+
reviewDecision: options.reviewDecision
|
|
5724
5749
|
} : getGitHubPullRequestCiSnapshot(octokit, repository, pullRequestNumber)
|
|
5725
5750
|
]);
|
|
5726
5751
|
return cacheGitHubPullRequestStatusSnapshot(repository, {
|
|
@@ -5729,7 +5754,8 @@ async function getGitHubPullRequestStatusSnapshot(octokit, repository, pullReque
|
|
|
5729
5754
|
hasUnresolvedReviewThreads: reviewThreadSummary.unresolvedReviewThreads > 0,
|
|
5730
5755
|
ciState: ciSnapshot.ciState,
|
|
5731
5756
|
mergeability: ciSnapshot.mergeability,
|
|
5732
|
-
mergeStateStatus: ciSnapshot.mergeStateStatus
|
|
5757
|
+
mergeStateStatus: ciSnapshot.mergeStateStatus,
|
|
5758
|
+
reviewDecision: ciSnapshot.reviewDecision
|
|
5733
5759
|
});
|
|
5734
5760
|
})();
|
|
5735
5761
|
activeGitHubPullRequestStatusSnapshotPromiseCache.set(cacheKey, loadSnapshotPromise);
|
|
@@ -10398,6 +10424,7 @@ async function buildProjectPullRequestSummaryRecord(octokit, repository, node, i
|
|
|
10398
10424
|
});
|
|
10399
10425
|
const inlineMergeability = normalizeGitHubPullRequestMergeability(node.mergeable);
|
|
10400
10426
|
const inlineMergeStateStatus = normalizeGitHubPullRequestMergeStateStatus(node.mergeStateStatus);
|
|
10427
|
+
const inlineReviewDecision = normalizeGitHubPullRequestReviewDecision(node.reviewDecision);
|
|
10401
10428
|
const [reviewThreadSummary, reviewSummary, statusSnapshot, behindBy] = await Promise.all([
|
|
10402
10429
|
getOrLoadCachedGitHubPullRequestReviewThreadSummary(
|
|
10403
10430
|
octokit,
|
|
@@ -10415,7 +10442,8 @@ async function buildProjectPullRequestSummaryRecord(octokit, repository, node, i
|
|
|
10415
10442
|
reviewThreadSummary: inlineReviewThreadSummary,
|
|
10416
10443
|
ciState: inlineCiState,
|
|
10417
10444
|
mergeability: inlineMergeability,
|
|
10418
|
-
mergeStateStatus: inlineMergeStateStatus
|
|
10445
|
+
mergeStateStatus: inlineMergeStateStatus,
|
|
10446
|
+
reviewDecision: inlineReviewDecision
|
|
10419
10447
|
}),
|
|
10420
10448
|
getGitHubPullRequestBehindCount(octokit, repository, {
|
|
10421
10449
|
baseBranch: node.baseRefName,
|
|
@@ -10555,6 +10583,7 @@ async function buildProjectPullRequestMetricCounts(octokit, repository, node, pu
|
|
|
10555
10583
|
});
|
|
10556
10584
|
const inlineMergeability = normalizeGitHubPullRequestMergeability(node.mergeable);
|
|
10557
10585
|
const inlineMergeStateStatus = normalizeGitHubPullRequestMergeStateStatus(node.mergeStateStatus);
|
|
10586
|
+
const inlineReviewDecision = normalizeGitHubPullRequestReviewDecision(node.reviewDecision);
|
|
10558
10587
|
const [reviewThreadSummary, reviewSummary, statusSnapshot] = await Promise.all([
|
|
10559
10588
|
getOrLoadCachedGitHubPullRequestReviewThreadSummary(
|
|
10560
10589
|
octokit,
|
|
@@ -10572,7 +10601,8 @@ async function buildProjectPullRequestMetricCounts(octokit, repository, node, pu
|
|
|
10572
10601
|
reviewThreadSummary: inlineReviewThreadSummary,
|
|
10573
10602
|
ciState: inlineCiState,
|
|
10574
10603
|
mergeability: inlineMergeability,
|
|
10575
|
-
mergeStateStatus: inlineMergeStateStatus
|
|
10604
|
+
mergeStateStatus: inlineMergeStateStatus,
|
|
10605
|
+
reviewDecision: inlineReviewDecision
|
|
10576
10606
|
})
|
|
10577
10607
|
]);
|
|
10578
10608
|
const checksStatus = statusSnapshot.ciState === "green" ? "passed" : statusSnapshot.ciState === "red" ? "failed" : "pending";
|