patchrelay 0.29.0 → 0.29.2

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "service": "patchrelay",
3
- "version": "0.29.0",
4
- "commit": "64724ffea08f",
5
- "builtAt": "2026-03-31T23:12:33.806Z"
3
+ "version": "0.29.2",
4
+ "commit": "c98ff7a5985a",
5
+ "builtAt": "2026-04-01T00:13:06.178Z"
6
6
  }
@@ -23,10 +23,15 @@ export async function requestMergeQueueAdmission(params) {
23
23
  summary: `Queue hand-off requested via label "${protocol.admissionLabel}" on PR #${issue.prNumber}`,
24
24
  });
25
25
  try {
26
+ const [owner, repo] = protocol.repoFullName.split("/", 2);
27
+ if (!owner || !repo) {
28
+ throw new Error(`Invalid repoFullName: ${protocol.repoFullName}`);
29
+ }
26
30
  await execCommand("gh", [
27
- "pr", "edit", String(issue.prNumber),
28
- "--repo", protocol.repoFullName,
29
- "--add-label", protocol.admissionLabel,
31
+ "api",
32
+ "--method", "POST",
33
+ `repos/${owner}/${repo}/issues/${issue.prNumber}/labels`,
34
+ "-f", `labels[]=${protocol.admissionLabel}`,
30
35
  ], { timeoutMs: 15_000 });
31
36
  feed?.publish({
32
37
  level: "info",
@@ -526,7 +526,9 @@ export class RunOrchestrator {
526
526
  }
527
527
  // Review approved + checks not failed — advance to awaiting_queue
528
528
  if (issue.prReviewState === "approved" && issue.prCheckStatus !== "failed") {
529
- this.advanceIdleIssue(issue, "awaiting_queue", { clearFailureProvenance: true });
529
+ if (issue.factoryState !== "awaiting_queue") {
530
+ this.advanceIdleIssue(issue, "awaiting_queue", { clearFailureProvenance: true });
531
+ }
530
532
  continue;
531
533
  }
532
534
  // Checks failed + idle — route based on durable GitHub failure provenance.
@@ -788,9 +790,23 @@ export class RunOrchestrator {
788
790
  else if (run.runType === "review_fix" && issue.reviewFixAttempts > 0) {
789
791
  this.db.upsertIssue({ projectId: issue.projectId, linearIssueId: issue.linearIssueId, reviewFixAttempts: issue.reviewFixAttempts - 1 });
790
792
  }
791
- this.failRunAndClear(run, "Codex turn was interrupted");
793
+ const recoveredState = resolvePostRunState(this.db.getIssue(run.projectId, run.linearIssueId) ?? issue);
794
+ this.failRunAndClear(run, "Codex turn was interrupted", recoveredState);
792
795
  const failedIssue = this.db.getIssue(run.projectId, run.linearIssueId) ?? issue;
793
- void this.emitLinearActivity(failedIssue, buildRunFailureActivity(run.runType, "The Codex turn was interrupted."));
796
+ if (recoveredState) {
797
+ this.feed?.publish({
798
+ level: "info",
799
+ kind: "stage",
800
+ issueKey: issue.issueKey,
801
+ projectId: run.projectId,
802
+ stage: recoveredState,
803
+ status: "reconciled",
804
+ summary: `Interrupted ${run.runType} recovered \u2192 ${recoveredState}`,
805
+ });
806
+ }
807
+ else {
808
+ void this.emitLinearActivity(failedIssue, buildRunFailureActivity(run.runType, "The Codex turn was interrupted."));
809
+ }
794
810
  void this.syncLinearSession(failedIssue, { activeRunType: run.runType });
795
811
  return;
796
812
  }
@@ -881,14 +897,14 @@ export class RunOrchestrator {
881
897
  feed: this.feed,
882
898
  });
883
899
  }
884
- failRunAndClear(run, message) {
900
+ failRunAndClear(run, message, nextState = "failed") {
885
901
  this.db.transaction(() => {
886
902
  this.db.finishRun(run.id, { status: "failed", failureReason: message });
887
903
  this.db.upsertIssue({
888
904
  projectId: run.projectId,
889
905
  linearIssueId: run.linearIssueId,
890
906
  activeRunId: null,
891
- factoryState: "failed",
907
+ factoryState: nextState,
892
908
  });
893
909
  });
894
910
  }
@@ -146,6 +146,12 @@ export class WebhookHandler {
146
146
  if (isDelegationSignal && triggerAllowed && !activeRun && !existingIssue?.pendingRunType) {
147
147
  pendingRunType = "implementation";
148
148
  }
149
+ // Do not create tracked issue rows for unrelated Linear traffic.
150
+ // An issue becomes PatchRelay-relevant only once it is already tracked
151
+ // or a true delegation queues work.
152
+ if (!existingIssue && !pendingRunType) {
153
+ return { issue: undefined, desiredStage: undefined, delegated };
154
+ }
149
155
  // Resolve agent session
150
156
  const agentSessionId = normalized.agentSession?.id ??
151
157
  (!activeRun && (pendingRunType || (normalized.triggerEvent === "delegateChanged" && !delegated)) ? null : undefined);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "patchrelay",
3
- "version": "0.29.0",
3
+ "version": "0.29.2",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "repository": {