paperclip-github-plugin 0.8.3 → 0.8.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -3
- package/dist/manifest.js +2 -1
- package/dist/worker.js +65 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -159,8 +159,8 @@ When the local Paperclip API is available, the plugin also syncs labels by name,
|
|
|
159
159
|
| Open issue with no linked pull request, created by a repository maintainer | `todo` on first import |
|
|
160
160
|
| Open issue with no linked pull request | Configured default status, which defaults to `backlog` |
|
|
161
161
|
| Open issue with a linked pull request and unfinished CI | `in_progress` |
|
|
162
|
-
| Open issue with failing CI, a non-mergeable linked pull request,
|
|
163
|
-
| Open issue with green CI, a merge-ready linked pull request,
|
|
162
|
+
| 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 |
|
|
163
|
+
| Open issue with green CI, a merge-ready linked pull request, and all review threads resolved | `in_review` |
|
|
164
164
|
| Closed issue completed as finished work | `done` |
|
|
165
165
|
| Closed issue closed as `not_planned` or `duplicate` | `cancelled` |
|
|
166
166
|
|
|
@@ -170,7 +170,8 @@ Additional behavior:
|
|
|
170
170
|
- If the Paperclip host initially creates that imported maintainer issue in `backlog`, GitHub Sync promotes it to `todo` without replacing the configured default assignee with the executor handoff assignee, so triage ownership stays intact.
|
|
171
171
|
- When Paperclip board access is connected for a company, the advanced assignee dropdowns list both company agents and `Me` for the connected board user.
|
|
172
172
|
- 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.
|
|
173
|
-
- For linked pull requests, GitHub Sync treats merge-conflict, behind-branch, blocked, draft, unstable merge states, and
|
|
173
|
+
- For linked pull requests, GitHub Sync treats merge-conflict, behind-branch, blocked, draft, unstable merge states, and unresolved review threads as executor work, while merge-ready states such as `CLEAN` and `HAS_HOOKS` can move work into `in_review` when CI is green and review threads are resolved. A stale aggregate `CHANGES_REQUESTED` review decision alone does not move that maintainer wait back to active execution. Transient `UNKNOWN` mergeability also does not move an already `in_review` maintainer wait back to active execution when CI is green and review threads are resolved.
|
|
174
|
+
- Imported issues that are already `blocked` stay `blocked` while any first-class `blockedBy` issue is still non-terminal, even if the linked GitHub pull request is otherwise green and review-ready.
|
|
174
175
|
- 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.
|
|
175
176
|
- 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.
|
|
176
177
|
- 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.5"?.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,
|
|
@@ -559,6 +559,7 @@ var manifest = {
|
|
|
559
559
|
"issues.create",
|
|
560
560
|
"issues.update",
|
|
561
561
|
"issues.wakeup",
|
|
562
|
+
"issue.relations.read",
|
|
562
563
|
"issue.comments.read",
|
|
563
564
|
"issue.comments.create",
|
|
564
565
|
"agents.read",
|
package/dist/worker.js
CHANGED
|
@@ -1447,7 +1447,16 @@ function formatGitHubIssueCountLabel(count) {
|
|
|
1447
1447
|
return `${normalizedCount} GitHub ${normalizedCount === 1 ? "issue" : "issues"}`;
|
|
1448
1448
|
}
|
|
1449
1449
|
function getErrorMessage(error) {
|
|
1450
|
-
|
|
1450
|
+
if (error instanceof Error) {
|
|
1451
|
+
return error.message;
|
|
1452
|
+
}
|
|
1453
|
+
if (error && typeof error === "object" && "message" in error) {
|
|
1454
|
+
const message = error.message;
|
|
1455
|
+
if (typeof message === "string") {
|
|
1456
|
+
return message;
|
|
1457
|
+
}
|
|
1458
|
+
}
|
|
1459
|
+
return String(error);
|
|
1451
1460
|
}
|
|
1452
1461
|
function isPaperclipLabelSyncError(error) {
|
|
1453
1462
|
return error instanceof PaperclipLabelSyncError;
|
|
@@ -4889,7 +4898,9 @@ function resolvePaperclipStatusFromLinkedPullRequests(linkedPullRequests, option
|
|
|
4889
4898
|
)) {
|
|
4890
4899
|
return options?.preferInProgress ? "in_progress" : "todo";
|
|
4891
4900
|
}
|
|
4892
|
-
if (linkedPullRequests.length > 0 && linkedPullRequests.every(
|
|
4901
|
+
if (linkedPullRequests.length > 0 && linkedPullRequests.every(
|
|
4902
|
+
(pullRequest) => isGitHubPullRequestReviewReadyForSync(pullRequest) || options?.preserveTransientUnknownMergeabilityWait === true && isGitHubPullRequestTransientUnknownMergeabilityWait(pullRequest)
|
|
4903
|
+
)) {
|
|
4893
4904
|
return "in_review";
|
|
4894
4905
|
}
|
|
4895
4906
|
return "in_progress";
|
|
@@ -5021,6 +5032,40 @@ function getPaperclipIssueSyncContext(issue) {
|
|
|
5021
5032
|
executionState: normalizePaperclipIssueExecutionState(record.executionState)
|
|
5022
5033
|
};
|
|
5023
5034
|
}
|
|
5035
|
+
function hasUnresolvedPaperclipIssueBlockerSummary(blockers) {
|
|
5036
|
+
if (!Array.isArray(blockers)) {
|
|
5037
|
+
return false;
|
|
5038
|
+
}
|
|
5039
|
+
return blockers.some((blocker) => {
|
|
5040
|
+
if (!blocker || typeof blocker !== "object") {
|
|
5041
|
+
return true;
|
|
5042
|
+
}
|
|
5043
|
+
const status = blocker.status;
|
|
5044
|
+
return status !== "done" && status !== "cancelled";
|
|
5045
|
+
});
|
|
5046
|
+
}
|
|
5047
|
+
function isMissingIssueRelationsReadCapabilityError(error) {
|
|
5048
|
+
const message = getErrorMessage(error);
|
|
5049
|
+
return /missing required capability/i.test(message) && /issue\.relations\.read/i.test(message) && /issues\.relations\.get/i.test(message);
|
|
5050
|
+
}
|
|
5051
|
+
async function hasUnresolvedPaperclipIssueBlocker(ctx, issue, companyId) {
|
|
5052
|
+
const record = issue;
|
|
5053
|
+
if (hasUnresolvedPaperclipIssueBlockerSummary(record.blockedBy)) {
|
|
5054
|
+
return true;
|
|
5055
|
+
}
|
|
5056
|
+
if (typeof ctx.issues.relations?.get !== "function") {
|
|
5057
|
+
return false;
|
|
5058
|
+
}
|
|
5059
|
+
try {
|
|
5060
|
+
const relations = await ctx.issues.relations.get(issue.id, companyId);
|
|
5061
|
+
return hasUnresolvedPaperclipIssueBlockerSummary(relations.blockedBy);
|
|
5062
|
+
} catch (error) {
|
|
5063
|
+
if (isMissingIssueRelationsReadCapabilityError(error)) {
|
|
5064
|
+
return false;
|
|
5065
|
+
}
|
|
5066
|
+
throw error;
|
|
5067
|
+
}
|
|
5068
|
+
}
|
|
5024
5069
|
function isSamePaperclipIssueAssigneePrincipal(left, right) {
|
|
5025
5070
|
if (!left || !right) {
|
|
5026
5071
|
return !left && !right;
|
|
@@ -5392,7 +5437,8 @@ function resolvePaperclipIssueStatus(params) {
|
|
|
5392
5437
|
}
|
|
5393
5438
|
if (snapshot.linkedPullRequests.length > 0) {
|
|
5394
5439
|
return resolvePaperclipStatusFromLinkedPullRequests(snapshot.linkedPullRequests, {
|
|
5395
|
-
preferInProgress: hasExecutorHandoffTarget
|
|
5440
|
+
preferInProgress: hasExecutorHandoffTarget,
|
|
5441
|
+
preserveTransientUnknownMergeabilityWait: currentStatus === "done" || currentStatus === "in_review"
|
|
5396
5442
|
});
|
|
5397
5443
|
}
|
|
5398
5444
|
if (wasImportedThisRun) {
|
|
@@ -5409,7 +5455,8 @@ function resolvePaperclipPullRequestIssueStatus(params) {
|
|
|
5409
5455
|
return currentStatus;
|
|
5410
5456
|
}
|
|
5411
5457
|
return resolvePaperclipStatusFromLinkedPullRequests([pullRequest], {
|
|
5412
|
-
preferInProgress: hasExecutorHandoffTarget
|
|
5458
|
+
preferInProgress: hasExecutorHandoffTarget,
|
|
5459
|
+
preserveTransientUnknownMergeabilityWait: currentStatus === "in_review"
|
|
5413
5460
|
});
|
|
5414
5461
|
}
|
|
5415
5462
|
async function listLinkedPullRequestsForIssue(octokit, repository, issueNumber) {
|
|
@@ -5556,10 +5603,13 @@ function normalizeGitHubPullRequestReviewDecision(value) {
|
|
|
5556
5603
|
}
|
|
5557
5604
|
}
|
|
5558
5605
|
function isGitHubPullRequestActionRequiredForSync(pullRequest) {
|
|
5559
|
-
return pullRequest.
|
|
5606
|
+
return pullRequest.mergeability === "conflicting" || ACTION_REQUIRED_GITHUB_PULL_REQUEST_MERGE_STATE_STATUSES.has(pullRequest.mergeStateStatus);
|
|
5607
|
+
}
|
|
5608
|
+
function isGitHubPullRequestTransientUnknownMergeabilityWait(pullRequest) {
|
|
5609
|
+
return pullRequest.ciState === "green" && !pullRequest.hasUnresolvedReviewThreads && pullRequest.mergeability !== "conflicting" && pullRequest.mergeStateStatus === "unknown";
|
|
5560
5610
|
}
|
|
5561
5611
|
function isGitHubPullRequestReviewReadyForSync(pullRequest) {
|
|
5562
|
-
if (pullRequest.ciState !== "green" || pullRequest.hasUnresolvedReviewThreads
|
|
5612
|
+
if (pullRequest.ciState !== "green" || pullRequest.hasUnresolvedReviewThreads) {
|
|
5563
5613
|
return false;
|
|
5564
5614
|
}
|
|
5565
5615
|
return REVIEW_READY_GITHUB_PULL_REQUEST_MERGE_STATE_STATUSES.has(pullRequest.mergeStateStatus);
|
|
@@ -5592,9 +5642,6 @@ function listGitHubPullRequestSyncBlockingConditions(pullRequest) {
|
|
|
5592
5642
|
if (pullRequest.hasUnresolvedReviewThreads) {
|
|
5593
5643
|
conditions.push("unresolved review threads");
|
|
5594
5644
|
}
|
|
5595
|
-
if (pullRequest.reviewDecision === "changes_requested") {
|
|
5596
|
-
conditions.push("requested changes");
|
|
5597
|
-
}
|
|
5598
5645
|
return conditions;
|
|
5599
5646
|
}
|
|
5600
5647
|
function tryBuildGitHubPullRequestStatusSnapshotFromBatchNode(node, repository) {
|
|
@@ -9025,7 +9072,7 @@ async function synchronizePaperclipIssueStatuses(ctx, octokit, repository, mappi
|
|
|
9025
9072
|
paperclipIssueSyncContext,
|
|
9026
9073
|
advancedSettings
|
|
9027
9074
|
);
|
|
9028
|
-
|
|
9075
|
+
let nextStatus = resolvePaperclipIssueStatus({
|
|
9029
9076
|
currentStatus: paperclipIssue.status,
|
|
9030
9077
|
snapshot,
|
|
9031
9078
|
hasTrustedNewComment,
|
|
@@ -9034,6 +9081,9 @@ async function synchronizePaperclipIssueStatuses(ctx, octokit, repository, mappi
|
|
|
9034
9081
|
maintainerAuthoredImportedIssue,
|
|
9035
9082
|
hasExecutorHandoffTarget: Boolean(executorTransitionAssignee)
|
|
9036
9083
|
});
|
|
9084
|
+
if (paperclipIssue.status === "blocked" && nextStatus !== "blocked" && await hasUnresolvedPaperclipIssueBlocker(ctx, paperclipIssue, mapping.companyId)) {
|
|
9085
|
+
nextStatus = "blocked";
|
|
9086
|
+
}
|
|
9037
9087
|
const shouldPreserveMaintainerWaitRouting = isHealthyMaintainerWaitTransition({
|
|
9038
9088
|
currentStatus: paperclipIssue.status,
|
|
9039
9089
|
nextStatus,
|
|
@@ -9236,11 +9286,14 @@ async function synchronizePaperclipPullRequestIssueStatuses(ctx, octokit, mappin
|
|
|
9236
9286
|
paperclipIssueSyncContext,
|
|
9237
9287
|
advancedSettings
|
|
9238
9288
|
);
|
|
9239
|
-
|
|
9289
|
+
let nextStatus = resolvePaperclipPullRequestIssueStatus({
|
|
9240
9290
|
currentStatus: paperclipIssue.status,
|
|
9241
9291
|
pullRequest,
|
|
9242
9292
|
hasExecutorHandoffTarget: Boolean(executorTransitionAssignee)
|
|
9243
9293
|
});
|
|
9294
|
+
if (paperclipIssue.status === "blocked" && nextStatus !== "blocked" && await hasUnresolvedPaperclipIssueBlocker(ctx, paperclipIssue, mapping.companyId)) {
|
|
9295
|
+
nextStatus = "blocked";
|
|
9296
|
+
}
|
|
9244
9297
|
const shouldPreserveMaintainerWaitRouting = isHealthyMaintainerWaitTransition({
|
|
9245
9298
|
currentStatus: paperclipIssue.status,
|
|
9246
9299
|
nextStatus,
|
|
@@ -14327,6 +14380,7 @@ function shouldStartWorkerHost(moduleUrl, entry = process.argv[1]) {
|
|
|
14327
14380
|
}
|
|
14328
14381
|
var __testing = {
|
|
14329
14382
|
buildSyncFallbackExecutionStatePatch,
|
|
14383
|
+
hasUnresolvedPaperclipIssueBlocker,
|
|
14330
14384
|
isHealthyMaintainerWaitTransition,
|
|
14331
14385
|
resolveSyncTransitionAssignee
|
|
14332
14386
|
};
|