paperclip-github-plugin 0.8.3 → 0.8.4

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 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, requested changes, 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, no requested changes, and all review threads resolved | `in_review` |
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 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.
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.
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.3"?.trim() || typeof packageJson.version === "string" && packageJson.version.trim() || process.env.npm_package_version?.trim() || "0.0.0-dev";
538
+ var MANIFEST_VERSION = "0.8.4"?.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
@@ -5021,6 +5021,29 @@ function getPaperclipIssueSyncContext(issue) {
5021
5021
  executionState: normalizePaperclipIssueExecutionState(record.executionState)
5022
5022
  };
5023
5023
  }
5024
+ function hasUnresolvedPaperclipIssueBlockerSummary(blockers) {
5025
+ if (!Array.isArray(blockers)) {
5026
+ return false;
5027
+ }
5028
+ return blockers.some((blocker) => {
5029
+ if (!blocker || typeof blocker !== "object") {
5030
+ return true;
5031
+ }
5032
+ const status = blocker.status;
5033
+ return status !== "done" && status !== "cancelled";
5034
+ });
5035
+ }
5036
+ async function hasUnresolvedPaperclipIssueBlocker(ctx, issue, companyId) {
5037
+ const record = issue;
5038
+ if (hasUnresolvedPaperclipIssueBlockerSummary(record.blockedBy)) {
5039
+ return true;
5040
+ }
5041
+ if (typeof ctx.issues.relations?.get !== "function") {
5042
+ return false;
5043
+ }
5044
+ const relations = await ctx.issues.relations.get(issue.id, companyId);
5045
+ return hasUnresolvedPaperclipIssueBlockerSummary(relations.blockedBy);
5046
+ }
5024
5047
  function isSamePaperclipIssueAssigneePrincipal(left, right) {
5025
5048
  if (!left || !right) {
5026
5049
  return !left && !right;
@@ -5556,10 +5579,10 @@ function normalizeGitHubPullRequestReviewDecision(value) {
5556
5579
  }
5557
5580
  }
5558
5581
  function isGitHubPullRequestActionRequiredForSync(pullRequest) {
5559
- return pullRequest.reviewDecision === "changes_requested" || pullRequest.mergeability === "conflicting" || ACTION_REQUIRED_GITHUB_PULL_REQUEST_MERGE_STATE_STATUSES.has(pullRequest.mergeStateStatus);
5582
+ return pullRequest.mergeability === "conflicting" || ACTION_REQUIRED_GITHUB_PULL_REQUEST_MERGE_STATE_STATUSES.has(pullRequest.mergeStateStatus);
5560
5583
  }
5561
5584
  function isGitHubPullRequestReviewReadyForSync(pullRequest) {
5562
- if (pullRequest.ciState !== "green" || pullRequest.hasUnresolvedReviewThreads || pullRequest.reviewDecision === "changes_requested") {
5585
+ if (pullRequest.ciState !== "green" || pullRequest.hasUnresolvedReviewThreads) {
5563
5586
  return false;
5564
5587
  }
5565
5588
  return REVIEW_READY_GITHUB_PULL_REQUEST_MERGE_STATE_STATUSES.has(pullRequest.mergeStateStatus);
@@ -5592,9 +5615,6 @@ function listGitHubPullRequestSyncBlockingConditions(pullRequest) {
5592
5615
  if (pullRequest.hasUnresolvedReviewThreads) {
5593
5616
  conditions.push("unresolved review threads");
5594
5617
  }
5595
- if (pullRequest.reviewDecision === "changes_requested") {
5596
- conditions.push("requested changes");
5597
- }
5598
5618
  return conditions;
5599
5619
  }
5600
5620
  function tryBuildGitHubPullRequestStatusSnapshotFromBatchNode(node, repository) {
@@ -9025,7 +9045,7 @@ async function synchronizePaperclipIssueStatuses(ctx, octokit, repository, mappi
9025
9045
  paperclipIssueSyncContext,
9026
9046
  advancedSettings
9027
9047
  );
9028
- const nextStatus = resolvePaperclipIssueStatus({
9048
+ let nextStatus = resolvePaperclipIssueStatus({
9029
9049
  currentStatus: paperclipIssue.status,
9030
9050
  snapshot,
9031
9051
  hasTrustedNewComment,
@@ -9034,6 +9054,9 @@ async function synchronizePaperclipIssueStatuses(ctx, octokit, repository, mappi
9034
9054
  maintainerAuthoredImportedIssue,
9035
9055
  hasExecutorHandoffTarget: Boolean(executorTransitionAssignee)
9036
9056
  });
9057
+ if (paperclipIssue.status === "blocked" && nextStatus !== "blocked" && await hasUnresolvedPaperclipIssueBlocker(ctx, paperclipIssue, mapping.companyId)) {
9058
+ nextStatus = "blocked";
9059
+ }
9037
9060
  const shouldPreserveMaintainerWaitRouting = isHealthyMaintainerWaitTransition({
9038
9061
  currentStatus: paperclipIssue.status,
9039
9062
  nextStatus,
@@ -9236,11 +9259,14 @@ async function synchronizePaperclipPullRequestIssueStatuses(ctx, octokit, mappin
9236
9259
  paperclipIssueSyncContext,
9237
9260
  advancedSettings
9238
9261
  );
9239
- const nextStatus = resolvePaperclipPullRequestIssueStatus({
9262
+ let nextStatus = resolvePaperclipPullRequestIssueStatus({
9240
9263
  currentStatus: paperclipIssue.status,
9241
9264
  pullRequest,
9242
9265
  hasExecutorHandoffTarget: Boolean(executorTransitionAssignee)
9243
9266
  });
9267
+ if (paperclipIssue.status === "blocked" && nextStatus !== "blocked" && await hasUnresolvedPaperclipIssueBlocker(ctx, paperclipIssue, mapping.companyId)) {
9268
+ nextStatus = "blocked";
9269
+ }
9244
9270
  const shouldPreserveMaintainerWaitRouting = isHealthyMaintainerWaitTransition({
9245
9271
  currentStatus: paperclipIssue.status,
9246
9272
  nextStatus,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "paperclip-github-plugin",
3
- "version": "0.8.3",
3
+ "version": "0.8.4",
4
4
  "description": "Paperclip plugin for synchronizing GitHub issues into Paperclip projects.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",